a minimalist's unit testing framework
This project is maintained by seancorfield and jaycfields
adding signal, removing noise
clojure.test
compatibility is available via
clojure-expectations/clojure-test.
Clojure CLI/deps.edn
: com.github.seancorfield/expectations {:mvn/version "RELEASE"}
Leiningen/project.clj
: [com.github.seancorfield/expectations "RELEASE"]
There is a new expectations.clojure.test
namespace containing a defexpect
macro that
allows for named expectations to be defined, like clojure.test
's deftest
macro.
(ns example.test
(:require [expectations.clojure.test :refer :all]))
;; a single named expectation
(defexpect two 2 (+ 1 1))
;; a named group of expectations
(defexpect group
(expect [1 2] (conj [] 1 2))
(expect #{1 2} (conj #{} 1 2))
(expect {1 2} (assoc {} 1 2)))
Expectations defined this way can be run with regular clojure.test
tooling, such as lein test
or boot test
,
or via the "standard" test running commands within various Clojure-compatible
editors that are designed to work with clojure.test
.
All the regular clojure.test
functionality should be in play
at this point, including use-fixtures
instead of Expectations'
own in-context/before-run/after-run functionality.
The above code creates functions two
and group
that
have :test
metadata (containing the actual test expressions), and
the functions themselves run clojure.test/test-var
on themselves
so you can call (two)
and (group)
to run those tests
(and they will print out any failures), the same way deftest
works.
You can intermix other code inside defexpect
and you can nest expect
forms inside other code.
You can wrap blocks of code in expecting
to provide more descriptive test output -- mirroring clojure.test/testing
:
(ns example.test
(:require [expectations.clojure.test :refer :all]))
;; a single named expectation
(defexpect two 2 (+ 1 1))
;; a named group of expectations
(defexpect group
(expecting "conj should add each element"
(expect [1 2] (conj [] 1 2))
(expect #{1 2} (conj #{} 1 2)))
(expect {1 2} (assoc {} 1 2)))
Note that expectations.clojure.test
does not run tests on shutdown.
Instead, tests are run on-demand via the usual clojure.test
machinery and test runners.
Given the streamlined simplicity of expectations, you might wonder why you
would want to migrate your expectations test suite to clojure.test
-style
named tests? The short answer is tooling! Whilst we have
well-maintained, stable plugins for Leiningen and Boot, as well as an Emacs mode,
the reality is that Clojure tooling is constantly evolving and most of those
tools -- such as the excellent CIDER,
Cursive,
and the more recent ProtoREPL (for Atom) --
are going to focus on Clojure's built-in testing library first.
Support for the original form of Expectations, using unnamed tests, is
non-existent in Cursive, and can be problematic in other editors.
A whole ecosystem
of tooling has grown up around clojure.test
and to take advantage of
that with expectations, we either need to develop compatible extensions to each
and every tool or we need expectations to be compatible with clojure.test
.
One of the big obstacles for that compatibility is that, by default, expectations
generates "random" function names for test code (the function names are based on the
hashcode of the text form of the expect
body), which means the test
name changes whenever the text of the test changes. To address that, the new
expectations.clojure.test
namespace introduces named expectations via
the defexpect
macro (mimicking clojure.test
's deftest
macro). Whilst this goes against the Test Names
philosophy that expectations was created with, it buys us a lot in terms of
tooling support!
You can convert your test suite piecemeal, either replacing expect
with defexpect
(and a name) or wrapping several expect
forms with a single defexpect
(and a name). You can do this one
namespace at a time, but you'll need to run both types of tests
explicitly via tooling:
jfields$ lein do expectations, test jfields$ boot expectations test
In addition to broader tooling support, you also get access to anything that
is built on top of clojure.test
, such as JUnit XML output,
automatically.
What do you lose? Well, clojure.test
-based failure reporting
isn't as sophisticated as expectations-based failure reporting so you will
lose some of the clarity and expressiveness there.