Skip to content

chourave/mimolette

Repository files navigation

mimolette

[plumula/mimolette "0.2.1"] ;; latest release

Check your specs from clojure.test and cljs.test.

Here we check a-function and another-function against their specs:

(ns whatever.my-test
  (:require [whatever.my :refer [a-function another-function]] ; The functions we’re going to test

            [whatever.my.spec]                                 ; The specs for our functions are
                                                               ; in another namespace, load them

            [plumula.mimolette.alpha :refer [defspec-test]]))  ; Use the `alpha` version for recent
                                                               ; releases of spec

(defspec-test test-foo-spec `[a-function another-function])    ; Test `a-function` and `another-function`
                                                               ; againts their respective specs and name
                                                               ; the test `test-foo-spec`

Supported versions of Clojure(Script)

Mimolette works with Clojure 1.9.0-alpha8 and upwards, and ClojureScript 1.9.183 and upwards. This is because it relies on the spec.test/abbrev-result function.

For Clojure versions 1.9.alpha-8 to 1.9.alpha-15 and ClojureScript version 1.9.183 to 1.9.521, you need to require mimolette’s non-alpha namespace.

(ns whatever.my-test
  (:require [plumula.mimolette :refer [defspec-test]]))

Starting from Clojure 1.9.alpha-16 and ClojureScript 1.9.542, you need to require mimolette’s alpha namespace.

(ns whatever.my-test
  (:require [plumula.mimolette.alpha :refer [defspec-test]]))

This is a consequence of the Clojure team adding an alpha suffix to spec’s namespaces.

Usage

In your test namespace, require Mimolette – choosing either the alpha- or the non-alpha- namespace depending on your version of Clojure(Script). Mimolette works both for Clojure and ClojureScript.

You’re now ready to test your functions against their specs.

Testing functions

(defspec-test test-foo-spec `[a-function another-function])

The functions-to-be-tested must be named by fully-qualified symbols. Often, the most convenient way of getting them is to use the syntax quote as in the example above.

Testing a single function

You can drop the wrapping collection if you’re only checking a single function:

(defspec-test test-foo-spec `yet-another-function)

Testing whole namespaces

You might find the enumerate-namespace function useful for checking all the functions of one or more namespaces against their respective specs.

(defspec-test test-namespaces (stest/enumerate-namespace 'my.namespace))
(defspec-test test-namespaces (stest/enumerate-namespace '[my.other-namespace
                                                           yet-another.namespace]))

(assuming stest is an alias for spec.test).

Limiting the number of tests

If your test suite is getting too slow because of spec checks, you might want to restrict the number of values tested. For instance, this code would test my-function for 10 different inputs.

(defspec-test test-foo-spec `my-function {:opts {:num-tests 10}})

The underlying mechanism is a little convoluted. If you pass a map as third parameter to defspec-test, that parameter will get passed on as the opts to stest/check.

stest/check will in turn pass a special parameter on to quick-check. However, for some crazy reason, that parameter is named differently in Clojure (:clojure.spec.test.check/opts) and ClojureScript (:clojure.test.check/opts).

To remedy this, Mimolette will translate the namespace-less :opts key to whichever key the underlying specs implementation understands.

Finally, the :num-tests key adds another round of magic: it isn’t actually interpreted by quick-check. Instead, stest/check passes it as the num-tests parameter to quick-check.

Troubleshooting

###No tests are generated

defspec-test will only generate tests for specs that it knows about, so you want to make sur that your specs are actually loaded. If your specs reside in the same namespace as your functions, then that’s already taken care of. If your specs have a namespace of their own, then make sure to require that from your tests. This is especially true in ClojureScript, Clojure seems to be more forgiving here.

Known limitations

  • There is no test suite. Now that side-effect-free functions have been broken out of the original macro, they should be easier to test.
  • The handling of options is incredibly convoluted and ought to be straightened out.
  • You sometimes get the stack trace shown below. This probably happens when spec gives up generating input values after too many failed attempts, and should be handled more gracefully.
Uncaught exception, not in assertion.
expected: nil
  actual: java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.AssertionError

Project history and acknowledgements

The notable changes to this project are documented in the change log.

I (Frederic Merizen) am not the original author of this library.

The macro that evolved to become Mimolette made its first public appearance on November 4th 2016, when Kenny Williams published his ‘solution to integrating clojure.spec.test/check with clojure.test’ on the #clojure-spec channel of the clojurians slack.

He didn’t release the macro as a lib at the time, partly because it wasn’t clear if the practice it enabled was actually recommended, and partly because he felt that the core team might be working on something similar.

On November 19th 2016, Timothy Washington asked on Stack Overflow how to run spec checks from clojure.test. On November 21st he answered his own question with the defspec-test macro from the clojurians slack.

This is were I found the macro. I wanted to use it for my own projects. I’m no great fan of reuse by cut and paste, and so I decided to give it a home on Clojars, added ClojureScript support, and broke the macro down into simpler components, and thus Mimolette was born.

I would like to thank Kenny and Timothy for their feedback and helping me piece together this history.

License

Distributed under the MIT license. Copyright © 2017 Frederic Merizen & Kenny Williams.