test.check is a Clojure property-based testing tool inspired by QuickCheck. The core idea of test.check is that instead of enumerating expected input and output for unit tests, you write properties about your function that should hold true for all inputs. This lets you write concise, powerful tests.
Release notes for each version are available in
CHANGELOG.markdown
. Remember that prior to version
0.5.7, test.check was called simple-check.
As of version 0.9.0
, test.check requires Clojure >= 1.7.0
.
Please note a
breaking change for ClojureScript
in the 0.8.*
releases.
[org.clojure/test.check "0.10.0-alpha3"]
<dependency>
<groupId>org.clojure</groupId>
<artifactId>test.check</artifactId>
<version>0.10.0-alpha3</version>
</dependency>
[org.clojure/test.check "0.9.0"]
<dependency>
<groupId>org.clojure</groupId>
<artifactId>test.check</artifactId>
<version>0.9.0</version>
</dependency>
If you'd like to try a SNAPSHOT version, add the sonatype repository to your project.
Let's say we're testing a sort function. We want to check that that our sort
function is idempotent, that is, applying sort twice should be equivalent to
applying it once: (= (sort a) (sort (sort a)))
. Let's write a quick test to
make sure this is the case:
(require '[clojure.test.check :as tc])
(require '[clojure.test.check.generators :as gen])
(require '[clojure.test.check.properties :as prop #?@(:cljs [:include-macros true])])
(def sort-idempotent-prop
(prop/for-all [v (gen/vector gen/int)]
(= (sort v) (sort (sort v)))))
(tc/quick-check 100 sort-idempotent-prop)
;; => {:result true,
;; => :pass? true,
;; => :num-tests 100,
;; => :time-elapsed-ms 28,
;; => :seed 1528580707376}
In prose, this test reads: for all vectors of integers, v
, sorting v
is
equal to sorting v
twice.
What happens if our test fails? test.check will try and find 'smaller' inputs that still fail. This process is called shrinking. Let's see it in action:
(def prop-sorted-first-less-than-last
(prop/for-all [v (gen/not-empty (gen/vector gen/int))]
(let [s (sort v)]
(< (first s) (last s)))))
(tc/quick-check 100 prop-sorted-first-less-than-last)
;; => {:num-tests 5,
;; => :seed 1528580863556,
;; => :fail [[-3]],
;; => :failed-after-ms 1,
;; => :result false,
;; => :result-data nil,
;; => :failing-size 4,
;; => :pass? false,
;; => :shrunk
;; => {:total-nodes-visited 5,
;; => :depth 2,
;; => :pass? false,
;; => :result false,
;; => :result-data nil,
;; => :time-shrinking-ms 1,
;; => :smallest [[0]]}}
This test claims that the first element of a sorted vector should be less-than
the last. Of course, this isn't true: the test fails with input [-3]
, which
gets shrunk down to [0]
, as seen in the output above. As your test functions
require more sophisticated input, shrinking becomes critical to being able
to understand exactly why a random test failed. To see how powerful shrinking
is, let's come up with a contrived example: a function that fails if it's
passed a sequence that contains the number 42:
(def prop-no-42
(prop/for-all [v (gen/vector gen/int)]
(not (some #{42} v))))
(tc/quick-check 100 prop-no-42)
;; => {:num-tests 45,
;; => :seed 1528580964834,
;; => :fail
;; => [[-35 -9 -31 12 -30 -40 36 36 25 -2 -31 42 8 31 17 -19 3 -15 44 -1 -8 27 16]],
;; => :failed-after-ms 11,
;; => :result false,
;; => :result-data nil,
;; => :failing-size 44,
;; => :pass? false,
;; => :shrunk
;; => {:total-nodes-visited 16,
;; => :depth 5,
;; => :pass? false,
;; => :result false,
;; => :result-data nil,
;; => :time-shrinking-ms 1,
;; => :smallest [[42]]}}
We see that the test failed on a rather large vector, as seen in the :fail
key. But then test.check was able to shrink the input down to [42]
, as
seen in the keys [:shrunk :smallest]
.
To learn more, check out the documentation links.
clojure.test
IntegrationThe macro clojure.test.check.clojure-test/defspec
allows you to succinctly
write properties that run under the clojure.test
runner, for example:
(defspec first-element-is-min-after-sorting ;; the name of the test
100 ;; the number of iterations for test.check to test
(prop/for-all [v (gen/not-empty (gen/vector gen/int))]
(= (apply min v)
(first (sort v)))))
ClojureScript support was added in version 0.7.0
.
Integrating with cljs.test
is via the
clojure.test.check.clojure-test/defspec
macro, in the same fashion
as integration with clojure.test
on the jvm.
We can not accept pull requests. Please see CONTRIBUTING.md for details.
test.check runs in both jvm-clojure and clojurescript, so testing comprehensively requires several steps:
lein test
to run the JVM tests (requires Leiningen)lein cljsbuild once
to run the ClojureScript tests (also requires node.js)test-runners/run_tests_dev.html
and test-runners/run_tests_adv.html
and watch the
javascript console for outputscript/test-self-host
to run the self-hosted ClojureScript tests (also requires node.js)test.check used to be called simple-check.
See migrating from simple-check.
YourKit is kindly supporting test.check and other open source projects with its full-featured Java Profiler. YourKit, LLC is the creator of innovative and intelligent tools for profiling Java and .NET applications. Take a look at YourKit's leading software products:
Copyright © 2014 Rich Hickey, Reid Draper and contributors
Distributed under the Eclipse Public License, the same as Clojure.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close