std.test
import std.test
test.case("splits on commas", fn() {
test.eq(split("a,b", ","), ["a", "b"])
})
test.case("rejects an empty name", fn() {
test.err(fn() validate(""))
})
Run with ecko test.
Assertions
| call | passes when |
|---|---|
test.eq(actual, expected) | the two are equal |
test.ok(value) | the value is truthy |
test.err(fn) | calling fn raises |
test.fail(message) | never - an explicit failure |
assert(cond, message?) (a builtin) | the condition holds |
test.group(name, fn) | groups cases under a heading |
test.err takes a function, not an expression, because the expression would raise before the assertion could catch it.
Skipping a case
test.case("restores file owners", fn() {
if os.env("USER") != "root" { test.skip("needs root") }
restore_and_check_owners()
})
test.skip(reason) ends the case as skipped, printed as SKIP <name> - <reason> and counted apart in the summary. A skip is not a failure, so the run still exits 0. Use it for a case that cannot run where it is running - it needs root, a network, a tool that is not installed - instead of returning early, which reports the case ok when nothing was checked.
The reason is required. test.skip works by throwing an error with kind: "skip", so nothing after it in the case runs. If something catches that error and the case then fails, the failure is what gets reported. Called outside a test.case, it is an error.
Discovery
tests/*.ecko and *_test.ecko anywhere in the tree. Both conventions work; see ecko test for why packages sometimes use one over the other.
ai in tests
ecko test forces mock mode and ignores your API key. So a test over an AI pipeline is deterministic, offline and free:
type Sentiment = Positive | Negative | Neutral
fn classify(text) = ai[Sentiment] "How does this review feel? {text}"
test.case("classifies sentiment", fn() {
test.eq(classify("great service"), Positive)
})
ai[Sentiment] returns the first variant offline, so this asserts the plumbing - that the call is wired up, the type coerces, and the branch is reachable. It does not assert that the model is any good, which no offline test can.
For output quality, use contracts: boolean contracts are real checks and they run in tests identically to production.
Determinism
Because assertions compare output, the usual rules apply: sort before comparing where order is not guaranteed, and avoid wall-clock values in expectations. Maps already iterate and encode in sorted key order, so map comparisons are stable.