clojure-spec

About: http://clojure.org/about/spec Guide: http://clojure.org/guides/spec API: https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html
2020-05-29T04:29:24.056Z

How do You eval? I actually want the default generator because I override it, but for testing i would also like to test the generic one.

alexmiller 2020-05-29T13:14:49.056400Z

with the eval function?

alexmiller 2020-05-29T13:15:09.056800Z

(s/gen (eval (s/form ::a)))

jacklombard 2020-05-29T19:30:59.057200Z

Hello, I want to introduce clojure spec to validate API endpoints. The parsed response naturally does not have namespaced keywords. How do I go about validating the data? Should I simply use unqualified keys? Where should I keep my spec?

jacklombard 2020-05-29T19:31:36.057900Z

I know it is a very common question of where to put spec but can't find the right answer

alexmiller 2020-05-29T19:34:50.058300Z

I answered these in #beginners - in the future, it's best to put questions in just one channel

👍 1
2020-05-29T19:55:05.058600Z

Is it not a bad thing to use the eval function?

alexmiller 2020-05-29T20:16:13.058900Z

it's not inherently bad, it's just a tool

alexmiller 2020-05-29T20:17:20.059900Z

Clojure evals all of your expressions after all

practicalli-john 2020-05-29T20:58:14.064500Z

I have a simple function and an associated fdef specification, but am not getting an error when calling the function with incorrect arguments. The function should take a map that is a ::customer specification. Have I misunderstood something?

(defn customer-fullname
  "Return customer full name from customer details"
  [customer-details]
  (str (::first-name customer-details)
       "_"
       (::last-name customer-details)))

(spec/fdef customer-fullname
  :args (spec/cat :customer ::customer)
  :ret string?
  :fn #(= (:ret %)
          (str (::first-name :args) " " (::last-name :args))))

(spec/def ::first-name string?)
(spec/def ::last-name string?)
(spec/def ::email-address
  (spec/and string?
            #(re-matches #"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$" %)))

(spec/def ::customer
  (spec/keys
    :req [::first-name ::last-name ::email-address]))
I would have expected a call to the customer-fullname to fail when the wrong kind of arguments are passed
(customer-fullname "customer")

practicalli-john 2020-05-29T21:01:28.065300Z

The ::customer spec works with spec/valid? and spec/assert when I use them with :pre and :post conditions in a function definition, but seem to be missing something when using spec/fdef.

practicalli-john 2020-05-29T21:25:53.066600Z

Ah, I have now learned about instrumenting the fdef specifications, and now it works, well fails when I expect it too...

(spec-test/instrument `customer-fullname)

practicalli-john 2020-05-29T21:31:08.068100Z

I am not clear on if it is valuable to use fdef without instrumenting them. Some discussions suggest is it but I have not really understood why as yet. I am still not that clear on the :fn aspect of fdef so will be on the look out for more examples. I still have a lot to understand about spec.

alexmiller 2020-05-29T21:34:55.068700Z

you can see the specs in (doc customer-fullname)

seancorfield 2020-05-29T21:35:12.069100Z

@jr0cket You can also spec-test/check them, separate from instrumentation.

👍 1
alexmiller 2020-05-29T21:35:17.069200Z

^^

alexmiller 2020-05-29T21:35:35.069800Z

those are the main benefits

seancorfield 2020-05-29T21:36:35.070900Z

In particular, instrumentation checks :args passed in are correct (and ignores :ret and :fn). Check is generative and passes in conforming random arguments (per the :args spec) and then checks :ret and :fn are satisfied.

practicalli-john 2020-05-29T21:38:50.072300Z

I can see the specs in the docs, yes that is very useful (had forgotten that in my frustration to get the code to fail). Will try out spec-test/check in the morning, sound promising. Thank you both.