Fork me on GitHub

Hi spec'ers


I'm up against a plumbing issue it seems


I wrote a simple function to run generated tests in a test namespace:


(defn run-generated-tests
   (for [out  (st/check (st/enumerate-namespace 'my.namespace)
                        {:gen overrides})
         :let [ret (:clojure.spec.test.check/ret out)]]
     {:success? (:result ret)
      :output   ret})))


This works as expected


If I integrate this in tests like this:


(deftest generated-test
  (let [output (run-generated-tests)]
    (is (every? :success? output))))


I can do (run-tests 'my.namespace) at the repl, which also works as expected


but if I run lein test my.namespace


it fails with: Caused by: java.lang.ClassCastException: clojure.lang.AFunction$1 cannot be cast to clojure.lang.MultiFn


I fail to see what might cause this, since I can't reproduce at the repl.

Alex Miller (Clojure team)15:01:11

that’s the lein monkey patch / test.check bug

Alex Miller (Clojure team)15:01:49

add :monkeypatch-clojure-test false to your project.clj to bypass

Alex Miller (Clojure team)15:01:29

the consequence is losing the ability to run lein retest (which most people don’t do)


@alexmiller ah, sorry, I missed that


thanks for the heads up


i’m speccing some functions that take javascript objects of particular types as arguments and return them


i want to conform some data that includes functions of these fspecs


but i get error stating there is no generator available for my specs of the following form:


#(instance? js/AudioNode %)


Probably conforming an fspec involves generativelh testing it?


ok, think i might swap the fspecs out for fn?for what I’m doing


setting up generators for js objects of specific types seems fiddily


it's not hard if they can be constructed easily from data


e.g., with gen/fmap


I want to make a map spec, but with a custom generator that generates it with an extra key (so eg the spec might be for a map like {::a 1 ::b 2}, but when I generate one I want to generate it like {::a 1 ::b 2 ::c 3}). Right now I'm mucking around with creating a spec for the fuller map including ::c, pulling the generator from that, and using that generator in a with-gen (and I haven't even quite got that working yet). It just seems excessively ugly -- anyone see a better way?


I could create the generator essentially manually, generating {::a (gen/generator (s/gen ::a)) ...} but that seems ugly too, especially because I'm creating these specs dynamically on the fly, so it'd result in awfully complicated code.

Alex Miller (Clojure team)21:01:51

I’d probably do your first idea with grabbing the gen from the fuller spec - why is it looking ugly?

Alex Miller (Clojure team)21:01:24

if it’s repetition, you might try s/merge of the original spec with an additional map spec, then grabbing the gen from that


Oh, that's exactly the elegant sort of solution I was looking for 🙂. I think this'll be my first time actually using s/merge. Thanks, Alex!


merge is kinda awesome


The ugliness is just yeah, the repetition & all the lines of code, and creating a spec only to throw it away.


way nicer than typical inheritance schemes

Alex Miller (Clojure team)21:01:49

to get in the spec mindset… maps are sets of attributes. merge is the union.


@alexmiller i found union and intersection to be very confusing terms when talking about “semantic subtyping” and open maps - the problem is that there is a subtle distinction between the union of keys and the union of the sets of values that match the specification


and they have opposite implications


if you union the keys, you shrink the set of possible values

Alex Miller (Clojure team)21:01:57

yeah, don’t think about it that hard :)


i only share this information with you b/c i’ve seen the same confusion about the words “union” and “intersection” affect people working with TypeScript and Flow


that’s why i’m glad the name chosen was “merge"


Hmm. Now that I try to think it through further, I'm less sure I see how to use s/merge to make it better. Are you envisioning something like this?

(s/def ::full-spec (s/with-gen
                     (s/gen (s/merge ::smaller-spec ::extra-key-spec))))
That seems like it would require creating three specs in total instead of two...


But it's very possible I've just gotten muddled trying to think it through 😉


(where ::extra-key-spec is a spec for a map containing only the extra key)


Oh, wait, I can use s/spec to make most of that implicit, I think.


I have a function in some legacy code that I want to write an example based clojure.test test for… it returns a vector of various types and I thought I could write a spec for it. In an is definition the granularity of failure is just a boolean… so I could extend clojure.test to display the explain data (I’ve experimented with this once before). I did think I could instrument it with an fdef but obviously that only works for input values. Anyway just wondering if people are currently using spec for example based testing


(I realise it’s for generative testing)


Damn, I'm getting close, but I keep tripping over java.lang.IllegalArgumentException: No matching ctor found for class clojure.test.check.generators$such_that$fn__32821 when I try to eval the s/def (which I have to do because this is all getting defined at runtime)


I can just do the s/def in the REPL and it works properly. And the eval was working until I started trying to add this generator, so I'm screwing something up...


is there some kind of configuration I need to set up to make spec.test/check work?

(->> (st/check 'lookup-requests-for-day) st/summarize-results)
=> {:total 0}


Having just gotten some help myself, I wish I could help either of the people who just asked questions, but unfortunately I don't have an answer to either of those 😕


tjtolton: did you try with a back-quote?

(->> (st/check `lookup-requests-for-day) st/summarize-results))