unit-test.lat
This module provides basic testing capabilities. Modules which are
designed to be used as unit tests should implement the mixin
TestModule
and provide the desired unit tests with addTest
.
truthy (block).
Runs the block. This method expects to be run inside a unit test (or
somewhere where $fail
is defined). If the block produces a truthy
result, that result is returned. Otherwise, $fail
is called with a
FailedTest
object as the argument.
eq (a, b).
Compares (without evaluating) a
and b
for ==
equality. This
method expects to be run inside a unit test, or somewhere with a
$fail
method. If the two arguments are equal, True
is returned.
Otherwise, a FailedTest
object with an appropriate message is
passed to $fail
.
throws (exc) do (block).
Runs the block. This method expects to be run inside a unit test, or
somewhere with a $fail
method. If the block throws an exception of
the type exc
, then True
is returned. Otherwise, an appropriate
FailedTest
object is passed to $fail
.
TestModule := Mixin clone.
A test module is a module which provides unit tests. Test modules still function like regular modules, simply with the added feature of having a collection of runnable tests.
TestModule interface := '[clone, addTest, importTestsFrom, runTests].
TestModule unitTests.
Returns the array of unit tests the module provides.
TestModule clone.
Cloning a test module also duplicates its unitTests
slot.
TestModule inject (target).
Injecting TestModule
into a target object has the added side effect
that the target object receives a unitTests
slot whose value is a
newly allocated array. Additionally, the injection provides the target
object with a toString
method whose behavior is as follows: If
toString
is called on the injector object itself, then
"TestModule"
is returned; otherwise, the parent toString
method is
called. This latter behavior is to ensure that Parents hierarchy
will continue to look neat for TestModule
-injected objects. Consider
the behavior of hierarchy
on an average test module.
Parents hierarchy: use 'some-test-module.
;; In current versions of Latitude
==> [some-test-module, TestModule, Module, Object]
;; Without the specialized toString behavior
==> [TestModule, TestModule, Module, Object]
This is because the usual toString
for a module is inherited from
Module
, which would get overriden by the TestModule
injection.
TestModule addTest (name) do (block).
Adds a new test to the current module. The name should be a symbol, and the block should be a method. Two distinct unit tests on the same module should not share a name.
TestModule importTestsFrom (module).
Adds all of the tests belonging to the test module module
to self
.
TestModule runTests.
Runs all of the unit tests contained within this module. Each unit
test in self unitTests
is run via its call
method. If an uncaught
exception escapes the unit test scope, then the test is considered to
have failed. Otherwise, the test has passed if and only if it returns
a truthy value. In the case of a failed test, the falsy result is
printed out. At the end of running all of the tests, a brief summary
is printed out and a TestSummary
object is returned.
UnitTest := Proc clone.
A UnitTest
object encapsulates an individual unit test as a callable
procedure. They are usually constructed via UnitTest make
or
TestModule addTest ...
.
UnitTest toString := "UnitTest".
UnitTest name.
Returns the unit test’s name, which defaults to '()
. This slot can be
(and usually is) overridden in subobjects.
UnitTest pretty.
Returns the unit test’s name, as though by self name
.
UnitTest call.
Runs the unit test. If this method returns truthy, then the unit test
has passed. If this method returns falsy, then the unit test has
failed. In the latter case, the falsy return value will usually
contain some additional information about what has failed. The default
implementation on the UnitTest
object simply returns the constant
True
.
UnitTest make (block).
Given an evaluating block object, this method constructs a new unit
test. The unit test’s call
method will invoke block
(with self
bound to the unit test itself) in an environment where a $fail
method is defined. If the unit test returns normally, it is considered
to have passed.
$fail
shall be a method taking a single argument. If $fail
is
called with a falsy argument, the unit test will immediately fail
without running any further code. If $fail
is called with a truthy
argument, the unit test will terminate successfully, again without
running any further code. Put another way $fail
behaves like a
continuation object pointing to the end of the test case.
TestSummary := Object clone.
This structure object stores information about the results after running a collection of unit tests.
TestSummary toString := "TestSummary".
TestSummary passes := 0.
TestSummary fails := 0.
FailedTest := Object clone.
This object represents a unit test which has failed. Any falsy object
returned from a unit test’s call
method will be regarded as a failed
test, but FailedTest
objects specifically provide useful additional
information about the test which has failed.
FailedTest toString := "FailedTest".
FailedTest toBool := False.
FailedTest message := "FailedTest".
FailedTest pretty.
Returns a string representation of the failed test, similar to that of
an exception object, consisting of the name of the failed test
(toString
), followed by its message (message
).
FailedTest make (message).
Constructs a new FailedTest
object with the given message.