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

Once

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.

Every

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)

References

results matching ""

    No results matching ""