Fork me on GitHub
#clojure-spec
<
2020-06-12
>
practicalli-johnny03:06:36

How can I specify the number of tests run when using clojure.spec.test.alpha/check ? By default its running 1000 tests for a check against 1 spec (the playing cards example from https://clojure.org/guides/spec#_a_game_of_cards ) and takes around 80 seconds to complete. The docs mention :num-tests within clojure.spec.test.check/opts but either I have the syntax wrong or missing something

;; runs 1000 tests
(spec-test/check `deal-cards
                 {:num-tests 1})

;; java.lang.RuntimeException
;; Invalid token: ::clojure.spec-test-check/opts
(spec-test/check `deal-cards
                 {::clojure.spec-test-check/opts {:num-tests 1}})
Apart from Clojure 1.10.1, the project includes the dependency :extra-deps {org.clojure/test.check {:mvn/version "1.0.0"}} Requiring clojure.spec.test.check generates an error when the namespace is evaluated
java.io.FileNotFoundException
   Could not locate clojure/spec/test/check__init.class,
   clojure/spec/test/check.clj or clojure/spec/test/check.cljc on classpath.
Project code is at https://github.com/practicalli/spec-generative-testing if it helps...

seancorfield04:06:22

The option should be a qualified keyword -- but that doesn't mean a namespace exists.

seancorfield04:06:15

:clojure.spec.test.check/opts

seancorfield04:06:04

If you want to use ::stc you can introduce an alias:

user=> (alias 'stc (create-ns 'clojure.spec.test.check))
nil
user=> ::stc/opts
:clojure.spec.test.check/opts
user=>

seancorfield04:06:21

Your ::clojure.spec-test-check/opts is going to fail because :: will try to auto-resolve clojure.spec-test-check which is not an alias.

David Pham05:06:14

Spec2 seems so cool. Increased programmability is such a good advantage. I have been using custom macros to generate spec, and it was a bit odd.

Aron07:06:56

Sorry if this question has an easy answer discoverable that I missed, didn't do a deep dive. I see that https://github.com/clojure/spec-alpha2/wiki/Schema-and-select#select is available in alpha2 but not in alpha. Obviously lot of people use spec already, but which version?

mpenet07:06:02

"spec1" (spec.alpha)

mpenet07:06:21

it's not really clear what will/wont change in spec2 and there are also a few bugs

Aron07:06:15

Thanks! is there something similar to spec/select already existing, perhaps written by someone else, published under a different name?

seancorfield15:06:38

@ashnur Spec 2 will eventually become the official clojure.spec. It's just not ready for use yet as it is still being actively designed and changed. Spec 1 will remain available for everyone already using it. I don't think anyone has tried to copy Spec 2 -- because it is not yet complete (and why would anyone try to recreate an official part of Clojure when Rich himself hasn't fully figured out parts of the design?).

Aron16:06:21

I am just curious if I want to use select or something that does similar stuff to select, what are my best options currently.

seancorfield16:06:21

@ashnur If you're just building toy stuff or learning/experimenting, you could use Spec 2. It's just not ready for production use.

seancorfield16:06:05

We were tracking it at work, with a branch of our (95k lines) codebase, and I really like the changes in Spec 2 -- above and beyond the schema/`select` stuff -- but there's been a lot of churn in Spec 2 and Alex has said that Rich will likely overhaul s/fdef completely before it is released (and it may drop s/keys completely as well), so we stopped tracking it months ago.

seancorfield16:06:28

We're just going to wait for it to be "fully baked" at this point.

seancorfield16:06:07

Once Alex signifies that it is stable and just needs testing to help iron out the bugs, we'll pick it up again.

Alex Miller (Clojure team)16:06:25

I will signify that by making a release :)

Aron16:06:37

so, if I understand that correctly, there is nothing else that targets the same problem domain to be used in production in the interim?

seancorfield16:06:54

clojure.spec.alpha targets that domain and can be used in production. Spec 2 is the "next generation" of that and will be the preferred solution when it becomes ready.

seancorfield16:06:46

Spec 2 is definitely "better" than Spec 1 -- because it's designed to incorporate lessons learned from the first version. But Spec 1 definitely has value in production.

Aron16:06:56

I like what s/select does and would like to use something like it in production, even if it's not necessarily exactly the kind of s/select that is planned by Rich, since as I understand it, there are still months, maybe years until that version will be ready.

Alex Miller (Clojure team)16:06:40

god I hope it's not years :)

6
😶 1
misha17:06:27

how can not be expressed in spec? to maximize built-in generators reuse and composability (really just ability to wrap any spec in "it" without looking at spec/form I am wrapping)

misha17:06:42

(before you look at me funny, I am writing a translator from json-schema to clojure-spec, particularly https://json-schema.org/understanding-json-schema/reference/combining.html#not)

misha17:06:05

anything better than this?

(do
  (s/def ::foo string?)
  (s/def ::bar (s/with-gen
                 (complement (partial s/valid? ::foo)) 
                 #(s/gen any?)))
  (s/exercise ::bar))

Alex Miller (Clojure team)17:06:55

not is weird and I would generally avoid doing it :)

misha17:06:06

at this time, I am trying to generate spec as close to schema as possible, as code you than paste into file, and then might chose to change

misha17:06:43

Alex, is there an (out the box) way to conform unqualified map and get qualified conformed map back?

misha17:06:29

(do
  (s/def :my/foo string?)
  (s/def ::map (s/keys :req-un [:my/foo]))
  (s/magic-conform ::map {:foo "x"})  #_=> {:my/foo "x"})

seancorfield18:06:10

I'm not Alex @misha but I can't think of any easy way to do that. You'd probably have to derive the (qualified) keys from the s/form of the Spec, and then zipmap with a version of those keys that had been mapped to unqualified keys, and then use clojure.set/rename-keys on your validated data.

seancorfield18:06:00

(but that won't work with nested data structures/specs or anything more complex than just s/keys)

Alex Miller (Clojure team)18:06:51

I guess you still wouldn’t get unqual

Alex Miller (Clojure team)18:06:08

So I’ll go with no :)

misha18:06:11

I thought about just including both :req and :req-un sets of keys, but it does not solve "I have a map from example page, show me the specs it uses", and screws up the generators, which you probably want to generate either entirely qualified or entirely unqualified deep tree.

seancorfield19:06:13

If we were starting again from scratch with Spec available, and using next.jdbc instead of clojure.java.jdbc, I think we would only have unqualified keys at the boundary of our system: either as API input or user input (forms, URLs), and at outgoing boundaries for JSON-based systems. So our use of :req-un/`:opt-un` would be a lot smaller, and we'd explicitly transform validated input into a domain model that always used qualified keys. Interacting with JDBC via next.jdbc means you can use qualified keys going out to the DB and you would get qualified keys coming in from the DB as well, automatically.

Joshua Suskalo19:06:19

I'm playing around with custom generators, since there's a type which it seems like spec is having a hard time generating for some tests.

(s/def ::value pos-int?)
(s/def ::name keyword?)
(s/def ::symbol (s/keys :req [::value ::name]))
(s/def ::symbols (s/coll-of ::symbol :kind set?))
(s/def ::rows pos-int?)
(s/def ::columns (s/and pos-int?
                        #(>= % 3)))
(def machine-gen
  (gen/let [machine (gen/fmap
                     (fn [[cols rows]]
                       {::rows rows ::columns cols})
                     (gen/tuple (gen/fmap (partial + 3) gen/nat)
                                (gen/fmap inc gen/nat)))
            symbols (gen/vector-distinct (s/gen ::symbol)
                                         {:min-elements (inc (::rows machine))})]
    (assoc machine ::symbols symbols)))
(s/def ::machine (s/with-gen
                   (s/and (s/keys :req [::symbols ::rows ::columns])
                          #(> (count (::symbols %)) (::rows %)))
                   (constantly machine-gen)))
The problem is that whenever I try to sample the machine-gen, it works fine, but if I try to sample the result of (s/gen ::machine) it always says that a such-that isn't met after 100 tries. What would be the cause of this?

misha19:06:04

Sean, my initial motivation is exploration, specifically of https://vega.github.io/ So I want to generate spec from schema (which is 9999km long), then take an example json, and with magic-qualify-conform see, which spec is that, and then navigate through keywords and specs in my IDE, instead trying to find things in huge json schema: https://vega.github.io/schema/vega-lite/v4.json or https://vega.github.io/schema/vega/v5.json This, and, the usual spec goods: exercise, etc.

misha19:06:32

so it seems I'd have to come up with "qualiform" too.

seancorfield19:06:28

Yeah, I can definitely see the utility of this and it would be nice as an option in s/conform.

seancorfield19:06:27

I think it's interesting that Spec 2 takes a different approach, where you can specify unqualified keys inline in a hash map spec or else qualified keys in a schema, to be select'ed

Joshua Suskalo20:06:24

Search is being very unhelpful for this problem, seems like few people run into it. @seancorfield would you happen to know of anything I could do to debug this issue or to alter the generator so that it'll work?

seancorfield20:06:57

@suskeyhose what is gen/ in your code above? I gather it's not clojure.spec.gen.alpha...?

Joshua Suskalo20:06:23

It's clojure.test.check.generators

Joshua Suskalo20:06:56

My understanding was that clojure.spec.gen.alpha was just a namespace that re-exposed some of the test.check vars.

Joshua Suskalo20:06:55

Which seems to be true looking at the source.

seancorfield20:06:41

Hahaha... OK, it took me a while... What is ::symbols? What does it generate?

seancorfield20:06:54

And then in machine-gen, what type is symbols?

Joshua Suskalo20:06:35

symbols is just a set of ::symbol, which are just maps with ::name and ::value

seancorfield20:06:11

::symbols is a set. symbols in machine-gen is a vector.

Joshua Suskalo20:06:33

Oh boy, of course that's it

seancorfield20:06:04

That was a nice Friday afternoon debugging diversion -- thank you! 🙂

Joshua Suskalo20:06:48

Thanks for helping me out! I feel so dumb when I just get my types misaligned like that 🙃

seancorfield20:06:04

No worries. I couldn't see it either. And I was simplifying the ::machine spec trying to figure out what the problem was... I was quite bewildered by it!