1 Defining Tests
dxtr edited this page 2026-10-18 12:07:40 +02:00

Defining tests

A test is a named body of code registered in a global table at load time. The package it was defined in is its group. There are no suites, no fixtures and no dependencies between tests.

(deftest name body...)
(deftest (name &key timeout tags skip wrap serial) [docstring] body...)

deftest returns name, like defun, and expands to an ordinary macro — no eval, no code walking.

Options

All options ride on the name, defstruct-style; nothing is sniffed from the body.

Option Type Meaning
:timeout real, seconds The body is interrupted after this many seconds of wall time; the result is an error whose message is timed out after N s.
:tags list of keywords For selection with run's :tags and :exclude.
:skip string The test does not run; its status is :skip and this string is the reason.
:wrap list of forms Each is the head of a with- macro call; the body is spliced into the last one and each wraps the next.
:serial boolean The test never runs on a worker thread. See Parallel.

Semantics

  • The displayed name is package/name, both downcased, where package is the name of *package* at definition time (not the symbol's package). A test defined in package parser/test with name parse-numbers shows as parser/test/parse-numbers.
  • A description is a leading string followed by at least one more form; it is kept on the test object and shown by the verbose reporter. A body that is only a string is a body, not a description.
  • Declarations at the start of the body are legal, with and without :wrap. That is why :wrap splices the body rather than wrapping it in a progn.
  • Redefining a test with the same name replaces it in place and keeps its position in definition order. The order is otherwise the order of first definition.
  • The body runs inside a named lambda so it appears in backtraces as (test name), and the runner trims the backtrace there.

The :wrap option

:wrap is how you apply a with- macro to a whole test body. Each element is the head of a macro call; assay splices the body into the innermost one, and each head wraps the next:

(deftest (scratch-files :wrap ((with-db) (with-temp-dir (dir))))
  (is (probe-file dir)))

expands, in effect, to:

(with-db
  (with-temp-dir (dir)
    (is (probe-file dir))))

This is assay's whole fixture story: a with- macro plus unwind-protect is the fixture, and :wrap applies one. See Structure.

Examples

(deftest parse-empty-string
  (is (null (parse ""))))

(deftest (large-input :timeout 2 :tags (:slow))
  (let ((input (make-string 1000000 :initial-element #\9)))
    (must (parse input) "must not blow the stack")
    (is (= 1000000 (digit-count (parse input))))))

(deftest (parse-floats :skip "not implemented yet")
  (is (= 1.5 (parse "1.5"))))

The registry

Call Returns
(find-test name) the test object with that name (compared with eq), or NIL
(remove-test name) true if the test existed, after unregistering it
(clear-tests package-designator) unregisters every test defined in that package

A test object answers to test-name, test-package, test-description, test-source, test-timeout, test-tags, test-skip and test-serial-p. You rarely need these; the runner and reporters use them.

Next: Assertions.