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.