Test Suite Prototype

Testing with Mettle

Mettle is a unit and behavioral testing framework. It includes a robust set of assertions (and refutations, a la Ruby's MiniTest), pure mock objects, and a runner that makes testing easy. Create a test suite by creating a child of $mettle.test_suite_proto. Add tests (any verb that begins with "test" is a test). Run tests by calling "run_tests()".

N.B. Testing frameworks and tests are hard to secure. Remove your tests (and remove Mettle) before you ship!

Assertions and Refutations

Behavior of the object under test is checked using assertions and refutations. Assertions claim that an expression about the state or behavior of the object under test is true. Refutations, on the other hand, claim that an expression is false.

this:assert():equal(a, b)    => /* a & b must be equal */
this:refute():equal(a, b)    => /* a & b must NOT be equal */

In the example above, "equal()" is a predicate. Mettle includes a useful set of predicates. If used without a predicate, assert() and refute() test whether an expression is true or false.

Mocking

Mocks are useful for testing the interaction between objects. A mock responds in a predictable way to specific verb calls made by the object under test. When the test is complete, Mettle verifies that the expected interactions occurred.

this:mock("dummy")                     => /* a name is required */
this:mock("dummy", ["length" -> 5])    => /* responds to "length" and returns 5 */

In the example above, if the method length() is not subsequently called on the mock, the test will fail!

run_tests

none test_suite:run_tests([STR pattern])

Runs the tests in the test suite. If an optional pattern is specified, runs only the tests that match pattern.

test_suite:run_tests("initializing")

Test Environment Prototype

assert

ANON assertion this:assert([ANY value [, STR message]])

Used within a test suite test verb. If called with a value, raises an error if value is false. Otherwise, returns an assertion object. The optional message describes the test failure.

this:assert(1)    => /* success */
this:assert(0)    => /* raises error */
this:assert()     => assertion

refute

ANON refutation this:refute([ANY value [, STR message]])

Used within a test suite test verb. If called with a value, raises an error if value is true. Otherwise, returns an refutation object. The optional message describes the test failure.

this:refute(0)    => /* success */
this:refute(1)    => /* raises error */
this:refute()     => refutation

mock

ANON mock this:mock(STR name [, MAP verbs])

Used within a test suite test verb. Defines a mock object. The name is required. The optional map of verbs defines the verbs the mock responds to, and their return values.

this:mock("test")                               => /* empty mock */
this:mock("dummy", ["foo" -> 1, "bar" -> 2])    => /* mock that responds to foo() and bar() */

Assertion Prototype

test

none assertion:test(ANY value, LIST|STR message, ANY other)

Tests value. If value is false, raises an error.

When raising an error, the first argument to raise() is the magic string "-*-failed-*-". If message is a list, the second argument is the first element of the list (the assertion failure message); otherwise it is message. The third argument is other.

Refutation Prototype

test

none refutation:test(ANY value, LIST|STR message, ANY other)

Tests value. If value is true, raises an error.

When raising an error, the first argument to raise() is the magic string "-*-failed-*-". If message is a list, the second argument is the first element of the list (the refutation failure message); otherwise it is message. The third argument is other.

Common Predicates

case_matters

test test:case_matters(ANY arg)

If arg is true, makes the test case sensitive. By default, tests are not case sensitive.

this:assert():case_matters(0):equal("Foo", "foo")    => /* succeeds */
this:refute():case_matters(1):equal("Foo", "foo")    => /* succeeds */

equal

none test:equal(ANY val1, ANY val2)

Tests whether or not val1 and val2 are equal.

this:assert():equal(1, 1)    => /* succeeds */
this:refute():equal(1, 1)    => /* fails */

is_true

none test:is_true(ANY val)

Tests whether or not val is true.

this:assert():is_true(1)    => /* succeeds */
this:refute():is_true(1)    => /* fails */

is_false

none test:is_false(ANY val)

Tests whether or not val is false.

this:assert():is_false(0)    => /* succeeds */
this:refute():is_false(0)    => /* fails */

matches

none test:matches(STR subject, STR pattern)

Tests whether or not subject matches pattern using the match() builtin.

this:assert():matches("foobar", "oo")    => /* succeeds */
this:refute():matches("foobar", "aa")    => /* succeeds */

is_member has_value

none test:is_member(ANY element, LIST list)
none test:has_value(ANY value, MAP map)

Tests whether or not element (or value) is in list (or map).

this:assert():is_member(1, {1})         => /* succeeds */
this:refute():has_value(1, [1 -> 2])    => /* succeeds */

is_index has_key

none test:is_index(INT index, LIST list)
none test:has_key(ANY key, MAP map)

Tests whether or not index (or key) is in list (or map).

this:assert():is_index(1, {2})         => /* succeeds */
this:refute():has_key(1, [2 -> 1])     => /* succeeds */