Testing
mino includes a test framework written in mino itself. It follows the same conventions as clojure.test: named tests with deftest, assertions with is, and contextual grouping with testing.
The framework lives in test.mino at the project root and is loaded via (require "test") from test files.
Writing Tests
A test file is a normal .mino file that requires the framework and defines tests:
(require "test")
(deftest addition
(is (= 3 (+ 1 2))))
(deftest string-operations
(testing "concatenation"
(is (= "hello world" (str "hello" " " "world"))))
(testing "substring"
(is (= "ell" (subs "hello" 1 4)))))
(deftest error-handling
(is (thrown? (throw "expected error"))))API Reference
(deftest name & body)
Defines and registers a named test. The body contains one or more assertions. Tests are collected in registration order and executed when run-tests is called.
(is expr) / (is expr msg)
The core assertion macro. Returns the result of the expression. On failure, records the original form, an optional message, and expected vs. actual values.
Three assertion modes:
| Form | Passes when |
|---|---|
(is (= expected actual)) | Values are equal. Reports expected and actual on failure. |
(is (thrown? body...)) | Body throws an exception. Fails if no exception is raised. |
(is expr) | Expression is truthy (not nil or false). |
(testing desc & body)
Adds a context string to failure messages. Context strings nest and are joined with > in the output. Must appear inside a deftest.
(deftest collections
(testing "vectors"
(testing "equality"
(is (= [1 2 3] [1 2 3])))))
;; On failure: "vectors > equality"(run-tests)
Executes all registered tests and prints a summary. Each test runs in a try/catch so one failure does not prevent the rest from running. Calls (exit 1) on any failures or errors, (exit 0) on success.
Running Tests
# Run the test suite
./mino tests/run.mino
# Run under GC stress (collects on every allocation)
MINO_GC_STRESS=1 ./mino tests/run.mino
# Via Make
make test
make test-gc-stressTest File Organization
By convention, test files live in tests/ and are named *_test.mino. A runner file loads all test modules and calls run-tests:
;; tests/run.mino
(require "test")
(require "tests/arithmetic_test")
(require "tests/string_test")
;; ...
(run-tests)Each test file starts with (require "test") to load the framework. The require call is idempotent: the framework is loaded once and cached.
Output
On success:
152 tests, 376 assertions: 376 passed, 0 failed, 0 errorsOn failure, each failing assertion is reported with its test name, context path, the original form, and a diff:
Failures:
in addition
arithmetic > basic
(= 4 (+ 1 2))
expected: 4
actual: 3
10 tests, 12 assertions: 11 passed, 1 failed, 0 errorsTesting in the REPL
The test framework works in the REPL too. Load it, define a test, and call run-tests interactively:
mino> (require "test")
mino> (deftest quick-check (is (= 4 (+ 2 2))))
mino> (run-tests)
1 tests, 1 assertions: 1 passed, 0 failed, 0 errors