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
| Action | Meaning | Form |
|---|---|---|
Given | Bind an input the feature set under test will read | Given the <name> with <value>. |
When | Run a feature set and capture its result | When the <result> from the <feature-set>. |
Then | Assert the result equals an expected value | Then the <result> with <expected>. |
Assert | Assert a value directly, without a When | Assert 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!
| Flag | Effect |
|---|---|
--filter <pattern> | Run only the tests whose name matches |
--verbose | Show each assertion, not just the verdict |
--no-color | Plain output, for CI logs |
--record <file.jsonl> | Record statement events — this is what drives Solaro's live canvas during a run |
--compiled | Build 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.