liveBook · Manning

Clojure.test

As explained in Clojure: The Essential Reference

clojure.test is Clojure's standard unit-testing framework namespace.

`clojure.test` is the testing framework included with Clojure. It defines tests, checks expectations, groups related assertions, runs tests by var or namespace, and reports failures. Tests are attached to vars through metadata. The framework also supports fixtures and extensible assertions and reporting. section 22.9

The main tools serve different parts of a test:

  • `deftest` defines a test in the current namespace; `deftest-` makes it nonpublic. `with-test` defines a function and its test together. section 22.9
  • `is` checks a truthy expression and reports what failed; an optional message adds context. `testing` labels nested contexts, while `are` applies one assertion pattern to multiple input rows. section 22.9
  • `test-var` runs one test var. `run-tests` runs tests in a namespace and returns counts for tests, passes, failures and errors. `run-all-tests` can select namespaces with a regular expression. section 22.9

A failing numerical expectation

`(deftest sqrt-test (is (= 2 (sqrt 4)) "Expecting 2"))` defines a check; `(test-var #'sqrt-test)` runs it. The shown Newton-method `sqrt` returns `2.000000000000002` for `4`, so exact equality fails. The report includes the message, expected expression and actual result. This is a useful signal to choose an expectation suited to approximate computation. section 22.9

Assertions can also check exceptions with `thrown?` or `thrown-with-msg?`. A check that raises an unexpected exception is reported as an error rather than an ordinary failed expectation. For reusable numerical checks, `assert-expr` can extend `is` with a custom assertion and report. section 22.9

(require '[clojure.test :refer [is deftest] :as t])

(defmethod t/assert-expr 'roughly [msg form] ; #1
  `(let [op1# ~(nth form 1) ; #2
         op2# ~(nth form 2)
         tolerance# (if (= 4 ~(count form)) ~(last form) 2)
         decimals# (/ 1. (Math/pow 10 tolerance#))
         result# (< (Math/abs (- op1# op2#)) decimals#)]
     (t/do-report   ; #3
       {:type (if result# :pass :fail)
        :message ~msg
        :expected (format "%s should be roughly %s with %s tolerance"
                          op1# op2# decimals#)
        :actual result#})
     result#))

(deftest sqrt-test ; #4
  (is (roughly 2 (sqrt 4) 14))
  (is (roughly 2 (sqrt 4) 15)))

(t/test-var #'sqrt-test)

;; FAIL in (sqrt-test) (form-init205.clj:3)
;; expected: "2 should be roughly 2.000000000000002 with 1.0E-15 tolerance"
;;   actual: false

This `assert-expr` method adds a `roughly` assertion with a selectable tolerance. Its two square-root checks show how a stricter tolerance can turn a pass into a reported failure. section 22.9

A namespace can control which tests `test-ns` runs by defining `test-ns-hook`. Fixtures wrap test execution for setup and teardown; with `use-fixtures :each`, the wrapper runs around every test. These mechanisms help compose a suite without putting setup steps into each assertion. section 22.9

How Clojure: The Essential Reference covers it

How deep it goes
Explains definitions, assertions, runners, fixtures and an assertion extension, but defers good test-suite design.
Examples
Uses Newton-method square-root failures, namespace runner results, a custom approximate assertion and per-test fixture output.
What it assumes
Assumes familiarity with Clojure functions and namespaces while introducing test definitions and assertions through examples.
What you can do after
Apply it via clojure.test to write assertions, run selected tests, interpret failures and add fixtures or custom checks.
How it connects
Connects var metadata, namespaces, macros, exception assertions, numerical tolerance and fixtures to test execution.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Clojure: The Essential Reference. The section links point to the parts of the book it draws on.