Clojurians
#clojure-spec
<
2017-01-13
>

This page is not created by, affiliated with, or supported by Slack Technologies, Inc.

pyr09:01:38

Hi spec'ers

pyr09:01:46

I'm up against a plumbing issue it seems

pyr09:01:35

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

pyr09:01:36

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

pyr09:01:25

This works as expected

pyr09:01:47

If I integrate this in tests like this:

pyr09:01:28

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

pyr09:01:17

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

pyr09:01:30

but if I run lein test my.namespace

pyr09:01:02

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

pyr09:01:58

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

alexmiller15:01:11

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

alexmiller15:01:49

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

alexmiller15:01:29

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

pyr15:01:11

@alexmiller ah, sorry, I missed that

pyr15:01:45

thanks for the heads up

triss15:01:30

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

triss15:01:04

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

triss15:01:45

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

triss15:01:20

#(instance? js/AudioNode %)

gfredericks15:01:31

Probably conforming an fspec involves generativelh testing it?

triss15:01:43

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

triss15:01:14

setting up generators for js objects of specific types seems fiddily

gfredericks16:01:28

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

gfredericks16:01:40

e.g., with gen/fmap

eggsyntax21:01:56

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?

eggsyntax21:01:42

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.

alexmiller21:01:51

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

alexmiller21: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

eggsyntax21:01:15

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

bbloom21:01:35

merge is kinda awesome

eggsyntax21:01:44

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

bbloom21:01:53

way nicer than typical inheritance schemes

alexmiller21:01:49

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

bbloom21:01:36

@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

bbloom21:01:46

and they have opposite implications

bbloom21:01:55

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

alexmiller21:01:57

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

bbloom21:01:25

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

bbloom21:01:40

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

eggsyntax21:01:23

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
                     ::smaller-spec
                     (s/gen (s/merge ::smaller-spec ::extra-key-spec))))
That seems like it would require creating three specs in total instead of two...

eggsyntax21:01:42

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

eggsyntax21:01:09

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

eggsyntax21:01:37

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

rickmoynihan21:01:34

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

rickmoynihan21:01:45

(I realise it’s for generative testing)

eggsyntax22:01:08

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)

eggsyntax22:01:02

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...

tjtolton22:01:35

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}

eggsyntax22:01:22

Got it :smile:

eggsyntax22:01:28

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 :confused:

rickmoynihan22:01:01

tjtolton: did you try with a back-quote?

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