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

callpasses 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.