← Back to Documentation

Testing

Tests are feature sets that live in the same file as the code they exercise — Given, When, Then, and nothing else to set up.

Tests live beside the code they test

A test is a feature set whose business activity ends in Test (or Tests). There is no test directory, no test runner to configure, and no setup or teardown — everything inside the feature set is the test. The compiler strips them out of native binaries, so shipping them costs nothing.

(* Production code — included in the binary *)
(get-length: String Utils) {
    Compute the <len: length> from <text>.
    Return an <OK: status> with <len>.
}

(* Test code — stripped from the binary *)
(length-of-hello: String Utils Test) {
    Given the <text> with "hello".
    When the <len> from the <get-length>.
    Then the <len> with 5.
}

Given / When / Then

ActionMeaningForm
GivenBind an input the feature set under test will readGiven the <name> with <value>.
WhenRun a feature set and capture its resultWhen the <result> from the <feature-set>.
ThenAssert the result equals an expected valueThen the <result> with <expected>.
AssertAssert a value directly, without a WhenAssert the <value> with <expected>.

A full file — several tests over the same production feature set:

(addition-test: Calculator Test) {
    Given the <a> with 15.
    Given the <b> with 7.
    Compute the <sum> from <a> + <b>.
    Then the <sum> with 22.
}

(complex-expression-test: Calculator Test) {
    Given the <price> with 50.
    Given the <quantity> with 3.
    Given the <tax-rate> with 0.08.
    Compute the <subtotal> from <price> * <quantity>.
    Compute the <tax> from <subtotal> * <tax-rate>.
    Compute the <total> from <subtotal> + <tax>.
    Then the <total> with 162.
}

Running them

$ aro test ./Examples/Calculator

=== ARO Test Results ===

  PASS  addition-test (14ms)
  PASS  subtraction-test (<1ms)
  PASS  multiplication-test (<1ms)
  PASS  division-test (<1ms)
  PASS  complex-expression-test (<1ms)

------------------------
Total:  5
Passed: 5
Failed: 0

All tests passed!
FlagEffect
--filter <pattern>Run only the tests whose name matches
--verboseShow each assertion, not just the verdict
--no-colorPlain output, for CI logs
--record <file.jsonl>Record statement events — this is what drives Solaro's live canvas during a run
--compiledBuild a native test-harness binary and run the same tests through it, instead of the interpreter

Testing the binary you ship

By default aro test runs the interpreter. That is the fast loop, but it only tells you how aro run behaves. ARO promises that a compiled binary answers a program the same way — and --compiled is how you check:

$ aro test --compiled ./Examples/Calculator

=== ARO Test Results ===

  PASS  addition-test (2ms)
  FAIL  subtraction-test
        Expected difference to be 15, but was
  ...

Same tests, same report format, different engine — so a divergence shows up as a difference in the results rather than in the layout. The run above is real: a Compute whose result is named difference is read as a set operation by the compiled path. That is the class of bug the mode exists to find.

It works by building a test-harness binary: the same compiled code as a shipped binary, except the test feature sets are kept instead of stripped and main drives them instead of calling Application-Start — a test run must not bind ports or start watchers. aro build --tests ./MyApp -o myapp-test produces that binary directly; it exits non-zero when a test fails, which makes it something CI can keep. --compiled needs aro build, so it is unavailable on Windows, and --record stays interpreter-only.

Naming

A feature-set name cannot contain a reserved word — assert, when, to and friends are tokens, so (direct-assert: … Test) is a parse error. Name tests after what they check: length-of-blank, rejects-empty-cart.

Learn More

The full specification is ARO-0015: Testing Framework. Examples/Calculator and Examples/AssertDemo both ship with tests you can run.