Fork me on GitHub
#clojure-spec
<
2016-10-21
>
tengstrand07:10:21

If I want to make sure that a map does not contain a specific attribute, for example :user/password, how do I do that?

pyr08:10:12

hola specers!

pyr08:10:27

I finally bit the bullet and am moving some of my work to spec.

pyr08:10:32

I have a few questions

pyr08:10:19

My first one is how to "compose" or programmatically generate specs, I have a multi spec with a lot of repetitive code

pyr08:10:58

where each spec in the multi-spec has common members and a few things that vary

pyr08:10:45

so i'd like to be able to do something along the lines of

(defn base-op [& ks] (spec/keys :req (concat [::base1 ::base2] ks)))

pyr08:10:00

This doesn't work because spec/keys is a macro

pyr08:10:33

Any hint as to how to approach this?

pyr08:10:40

Right now my method is to go with macros:

pyr08:10:29

(defmacro base-op [& ks] `(spec/keys :req ~(concat [::base1 ::base2] ks)))

pyr08:10:19

This works but doesn't feel right, if there's a better approach, happy to hear about it 🙂

mpenet08:10:35

I guess you can use s/and

mpenet08:10:05

there's also s/merge for this purpose, but I can't say really, haven't used it much so far

mpenet08:10:46

(s/def ::foo string?)
(s/def ::bar string?)
(s/def ::base (s/keys :req [::foo]))
(s/def ::stuff (s/keys :req [::bar]))
(s/def ::n (s/and ::base ::stuff))

(s/valid? ::n {::foo "0" ::bar "1"}) =>  true

mpenet09:10:54

I did it wrong again: (gen/large-integer* {:min 1e6 ...}) -> Doubles (my fault I know), but maybe there could be an assert (or casting) there

mpenet09:10:27

Is it possible to "share" a generator value for multiple keys in a map (or values in a spec) -> ex: a user profile with N fields?

mpenet09:10:31

time to look up gen overrides I guess

jetzajac11:10:05

Hi! How do I override generators for test/check? doc says we can pass :gen parameter in there, but can’t find any example. passing something like (stest/check split-operation {:gen {:onair.ot/count (s/gen :onair.ot/bounded-count)}}) shows that it is not generator which is expected as a value, but what then?

jetzajac11:10:28

getting error like clojure.test.check.generators.Generator cannot be cast to clojure.lang.IFn

jetzajac11:10:30

the problem I’m trying to address is actually testing for interger overflow, I don’t care about it, but generator for int passes huge numbers to a function, which causes it to fail with overflow exception. bounding value with predicate in spec causes a fail in conforming results =(

jetzajac11:10:18

looks like (stest/check split-operation {:gen {:onair.ot/count #(s/gen :onair.ot/bounded-count)}}) helps 😃

hiredman18:10:55

clojure.spec.gen wraps clojure.test.check.generators, and the way it wraps it results in generators becoming no argument functions that return generators (this is to make the dependency on test.check optional). It can make it kind of confusing about when to use a test.check generator and when to use a function returning a generator, most notably when using the generator combinators in clojure.spec.gen

Alex Miller (Clojure team)18:10:09

the answer is to always use a function returning a generator

Alex Miller (Clojure team)18:10:43

I only find it to be confusing when using both spec.gen and test.check.generators at the same time

Alex Miller (Clojure team)18:10:50

so I mostly try not to do that :)

liamd19:10:25

i’m trying to s/exercise a function i spec’d and i’m getting: ExceptionInfo Unable to construct gen at: [] for: [email protected] clojure.core/ex-info (core.clj:4725) what does it mean “`at: []`"

hiredman20:10:32

user=> (require '[clojure.spec :as s])
nil
user=> (s/def ::foo (constantly false))
:user/foo
user=> (s/exercise ::foo)
ExceptionInfo Unable to construct gen at: [] for: :user/foo  clojure.core/ex-info (core.clj:4725)
user=> 

hiredman20:10:57

you have a predicate that spec doesn't know how to find the generator for

kenny20:10:24

Where are you guys putting your clojure.spec.test/check calls? Are they in an is where you check if :result is true?

hiredman20:10:30

'[]' is the path to the spec, so an empty path means the top level

kenny20:10:25

When using test.check I would use defspec. Is there some similar integration point with clojure.spec.test?

kenny20:10:25

Maybe something like this?

(defmacro defspec-test
  [name & test-check-args]
  (when t/*load-tests*
    `(def ~(vary-meta name assoc :test `(fn [] (clojure.spec.test/check [email protected])))
       (fn [] (t/test-var (var ~name))))))

liamd21:10:50

hm so it can’t find a generator for my function

liamd21:10:05

this is right beneath it:

(s/fdef option-settings->environment-variables
        :args :elb/option-settings
        :ret  :elb/option-setting
        :fn   #(= "EnvironmentVariables" (-> % :ret :option-name)))

liamd21:10:15

is that enough to give it a generator or am i missing something?

liamd21:10:20

oh i should probably use exercise-fn

liamd21:10:03

i followed this exactly https://asciinema.org/a/87157 to use exercise-fn and get:

IllegalArgumentException No implementation of method: :specize* of protocol: #'clojure.spec/Specize found for class: nil  clojure.core/-cache-protocol-fn (core_deftype.clj:583)
=/

jrheard23:10:50

@kenny i haven’t seen official guidance on this. have you seen the summarize-results function, though?

jrheard23:10:21

i’ve seen people assert on its return value in regular deftests

kenny23:10:26

I wrote a version of defspec that uses abbrev-result. So you could do something like

(defspec-test qc-myfn? `myfn)

jrheard23:10:47

haven’t seen abbrev-result

kenny23:10:50

It works for now. I'm curious what the official solution will be

jrheard23:10:00

me too! 😄

kenny23:10:16

It's clojure.spec.test/abbrev-result

kenny23:10:38

Also combined it with clojure.spec/explain-out

kenny23:10:34

clojure.test + generative tests don't fit perfectly -- it's not clear what the :expected and :actual values in the clojure.test report should be.

kenny23:10:20

In my case I used the :ret for :expected and :clojure.spec.test/val for :actual