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 is clojure.test, loaded via (require '[clojure.test :refer [deftest is testing]]) from test files. tests/test.clj is a compatibility entry point that delegates to clojure.test.

Writing Tests

A test file is a normal .clj file that requires the framework and defines tests:

(require "tests/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.

Four assertion modes:

FormPasses 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 (thrown-with-msg? <re> body...))Body throws and the thrown value's message matches the regex. The 4-arg JVM shape (thrown-with-msg? <Class> <re> body...) is also accepted; the class symbol is documentation-only (mino has no class hierarchy).
(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 task test

# Run under GC stress (collects on every allocation)
MINO_GC_STRESS=1 ./mino tests/run.clj

Test File Organization

By convention, test files live in tests/ and are named *_test.clj. A runner file loads all test modules and calls run-tests:

;; tests/run.clj
(require "tests/test")
(require "tests/arithmetic_test")
(require "tests/string_test")
;; ...
(run-tests)

Each test file starts with (require "tests/test") to load the framework. The require call is idempotent: the framework is loaded once and cached.

Output

On success:

1474 tests, 7038 assertions: 7038 passed, 0 failed, 0 errors

On 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 errors

Testing 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