Ion Fusion Documentation
Release 0.38a1-SNAPSHOT (2026-04-16T19:45:37.790Z)

Module /fusion/experimental/check

A lightweight unit testing library.

This module allows applications to build a suite of tests in the form of Fusion scripts, where each script uses this module to express a number of checks that each validate some aspect of the code under test.

For examples of these features in action, peruse Fusion's own test suite.

These APIs are inspired by those of RackUnit by Noel Welsh and Ryan Culpepper.

WARNING

This module contains unstable, experimental features. There is NO SUPPORT for this module.

Checks

Checks are the fundamental components of test scripts. A check executes some logic, and either returns void to express success, or throws a "check exception" to express failure. This library provides a range of primitive checks, and applications can compose them into higher-level checks with broader responsibilites.

In order to present helpful output when failures occur, all checks are syntactic forms. Nevertheless, they work like procedures: the arguments are treated as expressions and evaluated left-to-right. (They cannot, however, be used like first-class procedures and passed as runtime values.)

Expecting Exceptions

Exceptions can be tested using several expect_* forms, verifing that certain kinds of values are raised by some code under test. Unlike the check_* forms, these do not act like procedures: evaluation of the expression is implicitly delayed via thunk to allow an exception handler to be installed.

The expect forms observe the hierarchy of exception types. For example, expect_contract_error will succeed whenever expect_arity_error would succeed.

Defining New Checks

You can compose your own checks from those given here by using define_check. By doing so, failure reports will include the check name, source code location, and actual arguments. The entire active "stack" of checks is reported.

Failure of a check is induced via fail_check which raises a check exception that displays the current stack of check frames.

Here's some examples from the basic checks:

(define_check (fail)
  (fail_check))

(define_check (check op val1 val2)
  (unless (op val1 val2)
    (fail_check)))

(define_check (check_pred pred val)
  (unless (pred val)
    (fail_check)))

(define_check (check_same expected actual)
  (unless (same expected actual)
    (fail_check)))

For simple cases like these, there's define_simple_check and define_binary_check; the latter improves failure reporting by displaying the expected and actual values.

(define_simple_check (check_pred pred val)
  (pred val))

(define_binary_check (check_same expected actual)
  (same expected actual))
check syntax
(check op val1 val2 [message])

Checks that the result of (op val1 val2) is truthy.

See check_pred for a one-value variant.

check_annotations syntax
(check_annotations expected value [message])

Checks that the value has the expected annotations. expected must be a sequence of strings or symbols.

For example, these checks succeed:

(check_annotations [] null)
(check_annotations ["a"] (quote a::null))
(check_annotations (quote (a b)) (quote a::b::null))

This one fails because the expected and actual annotations are not in the same order:

(check_annotations ["b", "a"] (quote a::b::null))
check_false syntax
(check_false val [message])

Checks that val is false (as opposed to untruthy). Annotations are ignored.

check_null syntax
(check_null val [message])

Checks that val is any null.

check_pred syntax
(check_pred pred val [message])

Checks that the result of (pred val) is truthy.

See check for a two-value variant.

check_same syntax
(check_same expected actual [message])

Checks that (same expected actual) is truthy.

check_true syntax
(check_true val [message])

Checks that val is true (as opposed to truthy). Annotations are ignored.

check_void syntax
(check_void val [message])

Checks that val is void.

define_binary_check syntax
(define_binary_check (name expected actual) expr ...)

Defines a new check form that's invoked with expected and actual values, and an optional message.

Like define_simple_check, the new check succeeds if the result of the exprs is truthy. However, this form automatically adds the expected and actual values to the failure report.

The body may not use the new check recursively.

For example:

(define_binary_check (check_same expected actual)
  (same expected actual))

You should not use this for binary checks that are composed solely from other checks: most check-forms return void, not true/false, so this wrapping form will aways fail. Instead, use two-argument define_check. For example:

// Incorrect: check_more returns void, so this always fails.
(define_binary_check (check_stuff expected actual)
  (check_more foo expected actual))

// Correct: define_check ignores the result of its body.
(define_check (check_stuff expected actual)
  (check_more foo expected actual))
define_check syntax
(define_check (name arg ...) expr ...)

Defines a new check form that's invoked as:

(name arg_expr ... [message])

Note that the optional message arg is added automatically and is not part of the declared signature.

The body is typically composed of several other checks, all of which must pass for the new check to succeed. The body may not use the new check recursively. Any results from the body are ignored and the check form always returns void.

For example:

(define_check (check_comparisons lo hi)
  (check <  lo hi)
  (check <= lo hi)
  (check =  lo lo)
  (check =  hi hi)
  (check >= hi lo)
  (check >  hi lo))
define_simple_check syntax
(define_simple_check (name arg ...) expr ...)

Defines a new check form that's invoked as:

(name arg_expr ... [message])

Note that the optional message arg is added automatically and is not part of the declared signature.

The new check succeeds if the result of the exprs is truthy. The body may not use the new check recursively.

For example:

(define_simple_check (check_pred pred val)
  (pred val))
expect_any_raise syntax
(expect_any_raise expr [message])

Evaluates expr and checks that it throws any value. If no exception is thrown, or if another kind of exception is thrown, then the check fails.

expect_argument_error syntax
(expect_argument_error expr [message])

Evaluates expr and checks that it throws argument_error. If no exception is thrown, or if another kind of exception is thrown, then the check fails.

expect_arity_error syntax
(expect_arity_error expr [message])

Evaluates expr and checks that it throws arity_error. If no exception is thrown, or if another kind of exception is thrown, then the check fails.

expect_assertion_error syntax
(expect_assertion_error expr [message])

Evaluates expr and checks that it throws assertion_error. If no exception is thrown, or if another kind of exception is thrown, then the check fails.

expect_check_error syntax
(expect_check_error expr [message])

Evaluates expr and checks that it throws check_error. If no exception is thrown, or if another kind of exception is thrown, then the check fails.

expect_contract_error syntax
(expect_contract_error expr [message])

Evaluates expr and checks that it throws contract_error. If no exception is thrown, or if another kind of exception is thrown, then the check fails.

expect_error_exn syntax
(expect_error_exn expr [message])

Evaluates expr and checks that it throws error_exn. If no exception is thrown, or if another kind of exception is thrown, then the check fails.

expect_exn syntax
(expect_exn expr [message])

Evaluates expr and checks that it throws exn. If no exception is thrown, or if another kind of exception is thrown, then the check fails.

expect_result_error syntax
(expect_result_error expr [message])

Evaluates expr and checks that it throws result_error. If no exception is thrown, or if another kind of exception is thrown, then the check fails.

expect_syntax_error syntax
(expect_syntax_error top_form [message])

Passes the given top-level form to eval and checks that it throws a syntax_error exception (as thrown by wrong_syntax).

This form currently doesn't work as expected within a module: the evaluation context is not the module's namespace, because current_namespace isn't changed when compiling a module's body. To test for syntax errors in a module, wrap the module with this form. So don't do this:

(module test "/fusion"
  (expect_syntax_error <something>))

but do this instead:

(expect_syntax_error
  (module test "/fusion"
    <something>))
fail syntax
(fail [message])

A check that fails unconditionally.

fail_check procedure
(fail_check)

Raises a check_error, using the current context stack as populated by the dynamically enclosing checks.

is_check_error procedure
(is_check_error v)

Determines whether a value is a check_error exception, a subtype of error_exn. Returns true or false.