Fork me on GitHub

this spec (s/def ::string-num (s/and string? #(not= nil (re-find #"^[0-9]+$" %)))) is unsatisfiable to the generator. I wanted to make sure there wasn't a better way to spec out a string of numbers that could be satisfied before I wrote a custom generator


Hello. Is there a way to create connected spec generators? I have

(s/def ::user #{“user1” “user2”})
(s/def ::pwd #{“pwd1” “pwd2”})
(s/def ::acc (s/keys :req [::user ::pwd]))
I want generator that will returns me accounts like {::user “user1” ::pwd “pwd1”}, but with current setup it will return random user and pwd. I wonder - is there any way to somehow connect specs to generate valid accs for my context?


@artemyarulin is this not what you're seeing, or else what do you mean by "valid accs"?


@gfredericks Well I meant that valid accounts are pairs of user1 pwd1 and user2 pwd2 and not user1 pwd2 with such data. It was solved like this:

(def credentials {"user1" "pwd1" "user2" "pwd2"})
(s/def ::acc (-> credentials seq set))
(s/def ::user (-> credentials keys set)})
(s/def ::pwd (-> credentials vals set)})


oh; this seems like a strange way to make specs for user data


cool that it’s possible 🙂

Alex Miller (Clojure team)13:12:24

@artemyarulin: there is a screencast about "modeling" your data in creating generators


@alexmiller thanks, I’ll have a look


Hi everyone. I bought myself Living Clojure for a Christmas present (5 more sleeps to go!). Can anyone recommend a good "beginners" intro course to spec (as this isn't included in the book)? (Other than the standard docs)


I'm hoping to find something very beginner-friendly


Thanks in advance 😄


@yogidevbear I haven’t watched this myself, but I like Lambda Island in general, and their intro to spec is a freebie:


@artemyarulin seems like a s/and with the part containing the s/keys you're interested in with it's own gen might fit better imho


I had to do something like this for user profile data that had to return valid/realistic data (gender matching firstname, profile pic matching "fake" user etc)


in short, check out :gen in s/keys


./back to xmas stuff


@yogidevbear This is not a guide, but it’s been the most helpful for me to think of it this way — the question that helps get things going is: What am I trying to spec? So, I wrote this out in a table on my board, and it’s helped me immensely. First of all, remember that for all intents and purposes, every predicate is a spec. It answers yes or no when given something. If it’s an associative structure, like a map, you’ll use keys, map-of, and merge. If it’s “data”, you will use a set as a predicate (`#{}`), your own functions, built-in predicates like string?, pos-int?, and so on, or for ranges, int-of, double-of, and inst-of (for dates) If it’s a sequential structure (lists, vectors), you’ll use coll-of (where every element is validated by the same predicate), tuple (where each element may have a different predicate), or the regex operations which can be used to combine patterns of specs within the sequence: cat, *, +, ?, alt, keys*, &, and spec (for nesting structures) You will want to fdef some function specs as well. So, for me the hard part initially was taking this huge mass of functionality and finding “where do I start?” The above is a very rough, incomplete guide to what you should be using in the spec library to get started, based on what you specifically are trying to spec. Testing, instrumenting, generating data is another thing, but first learn the above and I think it will not be hard to transition from there.


to expand on the related key/val before, this ends up something like this:

(s/def ::user string?)
(s/def ::pwd string?)
(s/def ::acc (s/keys :req-un [::user ::pwd] :gen
                     #(gen/fmap first (gen/shuffle [{:user "user1" :pwd "pwd1"}
                                                    {:user "user2" :pwd "pwd2"}
                                                    {:user "user3" :pwd "pwd3"}]))))
at least the gen and spec parts are separated this way


you can do the same with gen/let or gen/one-of + 1 gen/return per pair


Thanks @tjg and @joshjones 👍 @joshjones I think I'm going to have to save your response somewhere so I can digest it further down the line once I'm a bit more familiar with Clojure and some of the terms you're using 😉


i’m a bit of a newcomer to spec, but when defining specs can we use other specs instead of predicates? I’m testing in CLJS and it seems that it’s allowed, but then exercise on coll-of seems to break.


The following is throwing an error in ClojureScript 1.9.293

(s/def ::a nat-int?)
(s/def ::many-as (s/coll-of ::a :kind set?))
(s/exercise ::many-as)


ah, checking a bit closer it seems that specifying :kind set? does not work


I spec'd out a function that takes a set and makes a power set, wanted to know if I'm doing this right


the funny thing is when you write an :fn spec for a function, now you have two functions to debug.


hm, spec can't capture side-effects, can it?


like if you had a function that fired rockets into space and you wanted test.check to be able to fail a case where there weren't rockets in the air?


yeah, like there's no way to capture rockets-fired = true in the spec definition


which makes sense, b/c spec isn't trying to be a type system like Haskell's or anything.


unless your function returned a map like {:num-rockets 5 :target "moon" :fired true} but then its actually your function that captured the side effect


yeah good point


@bsima you cannot capture effects outside a function (AFAIK), but you can capture the return value and its relationship with the args in the :fn portion of the fdef of course


I suppose you could use a :postcondition in the definition of the function, or check some outside state inside the function, since you have access to the state anyway.


@bsima I spec'd out a function that updates a global atom doesn't smell right


Since state is involved, there's no (good) way to avoid race conditions when verifying that the updated value is correct. Code elsewhere could have modified the atom.


also, the generator is pretty abusive and will throw large numbers at it until it breaks 😆


@joshjones well the scary thing is that it has passed test.check over 3k times now. I guess I'd have to use pmap to see if I can ferret out race conditions


i am guessing check runs the function sequentially, so no danger of seeing race conditions in this case