Fixtures: setup and tear down for unit tests
Unit tests may require the system to be in a particular state before running a test. The state may need to be reset after running a test such as a database
Fixtures allow you to run code before and after tests, to set up the context in which tests should be run. Consider when fixtures should be run, especially fixtures that take a noticeable time to setup or tear down.
Slow running unit tests lead to unit tests not being run so often and therefore limit their value.
Organize tests with test selectors
Tests with fixtures may be slower to run so separate them by using a test selector, a piece of meta data attached to a deftest
definition. For example, add the ^:database
meta data to test that require database fixtures (deftest ^:database db-bulk-upload). The test runner can be instructed to skip or focus on tests with specific meta data.
Defining a fixture
A fixture is a standard Clojure function which takes a function as an argument. The function passed as an argument is either an individual test or all tests in the namespace, depending on how the fixture is used.
(defn my-fixture [test-run]
;; Setup: define bindings, create state, etc.
(test-run) ;; Run the relevant tests for the fixture (see `use-fixtures`)
;; Tear-down: reset state to a known value
)
Requires for clojure.test fixtures
To use those fixtures in a test run, include the use-fixtures
function in the require expression for clojure.test
(ns domain.application-test
(:require [clojure.test :refer [deftest is testing use-fixtures]]))
As fixtures are simply Clojure functions there are no additional requirements to define those functions.
When to run fixtures
The use-fixtures
function defines when a fixture should be called when running the unit tests in each namespace. All Clojure unit test runners should support the use-fixtures
definitions when running the tests.
When | Description |
---|---|
(use-fixtures :once fixture1 fixture2) |
Run the fixtures once for the namespace. |
(use-fixtures :every fixture1 fixture2) |
Run the fixtures for each `deftest** in the namespace |
The setup in the fixture is run, followed by all the deftest
functions in the namespace, then the fixture tear-down is run.
Running a fixture once per namespace is useful for establishing a database connection or creating a particular state of data for all the unit tests to use.
The fixture setup is run before each deftest
function in the namespace. The fixture tear-down is run after each deftest
function.
Anonymous functions as fixtures
The use-fixtures
function can also include anonymous function as well as a namespace scoped functions (deftest
).
(use-fixtures :each (fn [f] #_setup... (f) #_teardown))
defn
functions are usually recommended unless the fixture code is relatively terse.
Example: development database
Define a fixture to reset the database before running a test and clear the database after each test.
The create-database
and delete-database
are helper functions that are part of the namespace under test.
(defn database-reset-fixture
"Setup: drop all tables, creates new tables
Teardown: drop all tables
SQL schema code has if clauses to avoid errors running SQL code.
Arguments:
test-function - a function to run a specific test"
[test-function]
(SUT/create-database)
(test-function)
(SUT/delete-database))
The fixture should be used for each unit test (deftest
) that is defined in the namespace the database-reset-fixture
is defined in.
(use-fixtures :each database-reset-fixture)