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))
Exported Bindings
checkcheck_annotationscheck_falsecheck_nullcheck_predcheck_samecheck_truecheck_voiddefine_binary_checkdefine_checkdefine_simple_checkexpect_any_raiseexpect_argument_errorexpect_arity_errorexpect_assertion_errorexpect_check_errorexpect_contract_errorexpect_error_exnexpect_exnexpect_result_errorexpect_syntax_errorfailfail_checkis_check_error
(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 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 val [message])
Checks that val is false (as opposed to untruthy).
Annotations are ignored.
(check_null val [message])
Checks that val is any null.
(check_pred pred val [message])
Checks that the result of (pred val) is truthy.
See check for a two-value variant.
(check_same expected actual [message])
Checks that (same expected actual) is truthy.
(check_true val [message])
Checks that val is true (as opposed to truthy).
Annotations are ignored.
(check_void val [message])
Checks that val is void.
(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 (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 (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 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 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 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 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 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 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 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 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 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 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 [message])
A check that fails unconditionally.
(fail_check)
Raises a check_error, using the current context stack as populated by the
dynamically enclosing checks.
(is_check_error v)
Determines whether a value is a check_error exception, a subtype of
error_exn. Returns true or false.