This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-08-30
Channels
- # admin-announcements (1)
- # aws (32)
- # bangalore-clj (1)
- # beginners (2)
- # boot (137)
- # cider (2)
- # clara (1)
- # cljs-dev (39)
- # cljsrn (20)
- # clojure (268)
- # clojure-berlin (20)
- # clojure-canada (37)
- # clojure-dev (8)
- # clojure-gamedev (6)
- # clojure-norway (2)
- # clojure-russia (55)
- # clojure-spec (130)
- # clojure-uk (39)
- # clojurebridge (1)
- # clojurescript (102)
- # cursive (20)
- # datomic (231)
- # editors (5)
- # editors-rus (8)
- # events (5)
- # funcool (12)
- # hoplon (31)
- # instaparse (57)
- # jobs (9)
- # lein-figwheel (4)
- # off-topic (2)
- # om (8)
- # om-next (30)
- # onyx (241)
- # planck (6)
- # protorepl (4)
- # re-frame (115)
- # reagent (7)
- # rum (9)
- # schema (1)
- # test-check (9)
- # untangled (24)
- # yada (20)
oh no.. I don’t think I know how to solve it with s/keys
… the problem where I need to have a spec for a map where values picked up from a vector and associated values are together
so this is how I am generating a map with test.check.generators
:
(g/let [acnt (g/elements ledger-accounts)]
(g/hash-map
:id (uuid-gen)
:account-name (-> acnt :account-name g/return)
:account-type (-> acnt :account-type g/return)
…
how can I do the same thing with spec?but it seems unlikely that you'll know all of the values of something like :account-name before runtime
also, I reckon the clojure spec screencasts by stuart holloway here https://www.youtube.com/channel/UCaLlzGqiPE2QRj6sSOawJRg might help. particularly the last one on customising generators
that’s the thing, account-names are predefined. and types are predefined. I need to have a map with bunch of keys along with name and type where name comes from that vector and type is the associated type (from the same vector)
ok, honestly it's strange enough that there's probably not a way to express it and still get generators so you'll probably need to specify the generator like you did above, and do the spec portion separate
I'd probably write the spec as
(s/def ::account-name (set (map :account-name leger-accounts)))
(s/def ::account-type (set (map :account-type ledger-accounts)))
(s/def ::id uuid?)
(s/def ::your-map (s/and (s/keys :req-un [::account-name ::account-type ::id] :gen YOUR-GENERATOR) #((set (select-keys ledger-accounts :account-name :account-type)) (select-keys % :account-name :account-type))))
@ag it sounds like you are expressing what have been called hybrid maps
Which you can spec with a merge of keys and every of tuples of key-value pairs
I've given some examples here in the past
Yeah I’m watching Stu’s screencast “customizing generators”, looking into gen/bind
, gen/tuple
etc.
so why for simple predicates generator won’t work:
(s/def ::more-than-five #(< 5 %))
(s/conform ::more-than-five 6)
(s/exercise ::more-than-five)
clojure.lang.ExceptionInfo: Unable to construct gen at: [] for: :user/more-than-five
@ag that would be a hard thing to support in general
if you do (s/and integer? #(< 5 %))
it will at least make an attempt
because clojure.spec has a registry of generators for some of the built-in predicates
erhm… how do I refer to a spec s/def
fed in other namespace? I am trying to put spec in .cljc file, is that possible at all?
I think I found it… so if spec is in foo.clj then require [foo :as f] … (s/exercise ::f/my-spec
still can’t figure out how to refer to spec (and use it without having to prefix it with namespace)
thanks all; clojure.spec/merge
is exactly what I was looking for. I even read the whole API but I was quite sleepy.
Hi. Using the latest ClojureScript version, I'd like to get all checkable syms (with cljs.spec.test/checkable-syms
, filter them and call clojure.spec.test/check
on each of them individually). However, if I do something like (doseq [sym syms] (st/check sym))
it tells me sym
is unresolvable. This works fine in Clojure but in ClojureScript, am I supposed to pass in something else than symbols?
I'm looking to make a spec that validates nested maps (e.g.` {:value {:foo :bar :key :value}}`).
So far I'm only able to get one level of nesting via spec/keys
You can use two different specs: one for the inner map and then use it in the outer map.
I also have the need to have different specs for value (e.g. http://clojure.org/guides/spec#_multi_spec) . For example {:value {:foo :bar}}
and {:value {:key :value}}
(s/def ::foo-value (s/keys :req-un [::foo]))
(s/def ::key-value (s/keys :req-un [::key]))
(s/def ::value (s/or :foo-value ::foo-value :key-value ::key-value))
(s/def ::outer (s/keys :req-un [::value]))
java.jdbc has such an example: https://github.com/clojure/java.jdbc/blob/master/src/main/clojure/clojure/java/jdbc/spec.clj#L27-L42
s/cat specifies a sequence. ":id" in that code is specifying a label rather than a map key
I think spec is intentionally trying to steer people away from inline specs like that
you could make a separate spec for the key and use req-un to allow unnamespaced keys if that's what you're looking for
@ag giving every spec a fully qualified name is one of the core design goals of spec, things are going to get difficult if you try not to follow it
guys, I need a good spec for values of milliseconds after the Unix epoch. i.e.:`clj-time.coerce/to-long`
mmm that’s too simple, I need to make sure values are of reasonable datetime range, something like from 1900 to 2100 maybe
should really use int-in
if you’re defining a spec (not just the predicate int-in?
), as then you will get generator support
and there is a new fn for extracting the ms - inst-ms
in core
so I would say (s/int-in 0 (inst-ms #inst "2100-01-01"))
assuming you’re cool with the year 2100 problem
inst-ms
calls into the Inst protocol, which is extended to both Date and (if you use JDK 8), Instant. Could also be extended to a JodaTime Instant.
I don’t understand why this is failing:
(s/def ::account-name string?)
(s/def ::account-type keyword?)
(s/def ::description string?)
(s/valid? (s/keys :req [::account-name ::description ::account-type])
{:account-name "pre-fund" :account-type :internal :description "Pre-Fund”})
it has them ^^
:: will autoresolve and fully qualify
oh you mean in the data
sorry! :)
Sorry for bugging you with bunch of noob questions. Trying to solve real problem with spec. If I don’t get this right sooner, would be asked to stop my experiments.
noob questions are good
I asked Rich a lot of noob questions at the beginning too :)
is there a way to pass original value (I dunno of spec) to custom-generator function, when it’s created with s/with-gen
?
something like (gen/generate (s/gen (s/with-gen ::account-balance-updated #(gen-account-balance-updated %))))
I guess I can refer to ::account-balance-updated
from inside the gen-account-balance-updated
, yet thinking if there’s more “generic” way
@ag in short, no
but you can define both the spec and the spec-with-custom-gen to do so
so I have a spec for a map structure, I need to use custom generator function where I would generate all the fields of that map, except of few selected, those should come from a predefined variable
it’s easiest to use gen/fmap
and source it with (s/gen (s/keys ::a ::b))
, then in the function just merge with the constant map
where ::a and ::b are the variable parts
We are having a weird problem: https://gist.github.com/eraserhd/0aca4172b34c3c64f2e17f8c9107174d
(s/def ::a int?)
(s/def ::b int?)
(s/def ::c string?)
(s/def ::m (s/keys :req [::a ::b ::c]))
(s/def ::m (s/with-gen (s/keys :req [::a ::b ::c]) (fn [] (gen/fmap #(merge {::c "xyz"} %) (s/gen (s/keys :req [::a ::b]))))))
(gen/sample (s/gen ::m))
;; (#:user{:c "xyz", :a -1, :b 0} #:user{:c "xyz", :a 0, :b -1} #:user{:c "xyz", :a -1, :b 0} #:user{:c "xyz", :a -1, :b -1} #:user{:c "xyz", :a 0, :b 0} #:user{:c "xyz", :a -10, :b 1} #:user{:c "xyz", :a 3, :b -2} #:user{:c "xyz", :a -1, :b 40} #:user{:c "xyz", :a 45, :b -2} #:user{:c "xyz", :a -2, :b -7})
@eraserhd what’s the point of that gen/tuple?
Could it be that the tuple is adding an extra layer of being a collection, i.e. instead of returning [task]
it is returning [[task]]
?
seems like you just need vector
in that case
or you might just want to fmap with vector inside the fn
@alexmiller Er, we found out that spec conforms the generator's results to the spec.
We had something with multiple values in the tuple, but deleted code until we found out where there was a problem.
yes, spec does not trust the generator
even custom gens must produce values valid for the spec
Hmmm… so what’s the best way to create fixtures (for lack of a better term) with spec? This could range anywhere from setting up an environment within which to run the tests to testing that the output from the function under test matches a value generated from a replaced/stubbed function.
check
seems really useful for relatively pure functions, but having to orchestrate a lot via instrument
seems clunky. Does anyone have any recommendations?
if you want fixtures and assertions, use a testing framework which has them ?
that is, wrap a clojure.test around instrument+check
instrument could go in a fixture too if it applies broadly
Yes, I’ve done a bit of that. I have gotten it to work, but I was a bit unsatisfied with the result. I felt that the end result was a bit clunky and harder to understand/maintain compared to just using a traditional deftest.
I will continue to experiment with it, but I was curious to see if anyone else had any experience with non-trivial testing using spec.
what was unsatisfying?
Well, I am still a bit of a spec beginner (have been using it for less than a week), so there are things I may be missing. Some things that could help include:
1. More documentation around instrument
, especially with examples of using the various options.
2. It would be nice to be able to call instrument
with just options rather than (instrument (instrumentable-syms) opts)
.
3. The lack of a with-instrumentation
macro makes setting up ‘fixtures’ harder. It was a simple one to write, but it would be nice to have it baked in.
4. Overall, specifying things through instrument
doesn’t read as well. When this sort of thing is written out long-hand in code, it is easier to understand how the flow works. Instrumentation is sort of like defining callbacks before you need to use them, which removes some of the context from them.
4. The last one it is a little bit harder to describe. Instrumenting is very good about modifying things that will happen during the execution of the function, but sometimes I want to do something around the execution of the function. I can do that by specifying a test function and running spec on that, but it would be nice if there was a more direct way of doing that with spec.
1. for sure :) more will be coming eventually. 2. did you find clojure.spec.test/enumerate-namespace? it helps with building sym lists. 3. there is a ticket for that that I was just looking at. there are questions - feel free to weigh in. http://dev.clojure.org/jira/browse/CLJ-2015 4. I can see what you mean, prob worth seeing more examples 4. that’s interesting. sounds like a check fixture? I assume you meant “running check”, not “running spec” above
woah CLJ tickets have exactly caught up with the calendar year
now if we can just slow down to 1 per year, that will always be true
sounds doable
Regarding (4), you are right. I set up a method to handle (is (checks?
foo/bar {…}))` where the last two arguments are just used to invoke stest/check
. I first started by putting my fixture in a separately defined test function that invoked the the function to test. It works, but adds a bit of noise (also there is not a way to automatically inherit the function spec of the original). In a case where the fixture is transparent, i.e. takes the same args/returns the same result, I was able to write a macro that too the var name’s symbol and a higher-order function that served as the test fixture. That macro dereferenced the var giving the old value a local binding, and with-redef
ed the var to invoke the fixture (which took the old binding as an argument and returned a new function that behaved like the old one plus the fixture logic).
That technique works when the fixture is transparent, but perhaps I want to create a fixture that has a different signature than the function I am testing. For example, I might be testing that a function will eventually invoke a stubbed function that will return generated data. I want to check that the return value of the function under test satisfies a predicate that is partly dependent on the data that comes from the stubbed function, not just the inputs.
This is all doable right now by creating such a function and properly specifying the test function and instrumenting the third-party function.
This is all doable right now by creating such a function and properly specifying the test function and instrumenting the stubbed function.
how can I create generate based on predefined vector? let’s say I have a vector [{:name “Anna”}{:name “David}] ..etc
, I need a generator that creates vector with the same amount of elements, with added fields let’s say :age
and :height
?
@ag so you want a generator that completes the maps?
You could use gen/vector with a fixed length and gen/hash-map
I need to create a bunch of accounts with some predefined fields and some randomly generated fields, and need to create bunch of other structures associated
(gen/fmap #(map merge % predefined) (gen/vector (gen/hash-map ...) (count predefined)))
@ag would something like ← that work?