On this page
test blocks
std.testing
The command
Compile-time tests
What the test suite of this book looks like
Tests
Tests live in the file with the code, run with one command, and are checked by the same compiler with the same rules. A test that does not compile is a failing test; a test that leaks is reported by nx leaks; a test that could have run at compile time, can.
import std.testing
/// Celsius from Fahrenheit.
fn to_celsius(f: f64) -> f64 { return (f - 32.0) * 5.0 / 9.0 }
/// The median of a sorted slice; null when it is empty.
fn median(sorted: []i32) -> ?i32 {
if sorted.len == 0 { return null }
let mid = sorted.len / 2
if sorted.len % 2 == 1 { return sorted[mid] }
return (sorted[mid - 1] + sorted[mid]) / 2
}
error Parse { Bad }
fn parse_bool(s: []u8) -> Parse!bool {
if s == "yes" { return true }
if s == "no" { return false }
return error.Bad
}
fn main() {
var readings: [3]i32 = [3, 1, 2]
readings[..].sort()
println("{:.1} {}", .{to_celsius(212.0), median(readings[..]) orelse -1})
}
test "freezing and boiling" {
expect_eq(to_celsius(32.0), 0.0)
expect_eq(to_celsius(212.0), 100.0)
}
test "body temperature, approximately" {
testing.expect_approx(to_celsius(98.6), 37.0, 0.01)
}
test "median of odd, even and empty" {
let odd = [1, 2, 3]
let even = [1, 2, 3, 4]
expect_eq(median(odd[..]) orelse -1, 2)
expect_eq(median(even[..]) orelse -1, 2)
let empty = List(i32).new()
expect(median(empty[..]) == null)
}
test "errors" {
expect(try parse_bool("yes"))
testing.expect_error(bool, parse_bool("maybe"), error.Bad)
}
test "text" {
testing.expect_contains("base camp", "camp")
testing.expect_lines("a\nb\n", "a\nb\n")
}
comptime test "runs while compiling" {
expect_eq(median([5][..]) orelse 0, 5)
}
$ nx test topo/code/testing.nx
ok freezing and boiling
ok body temperature, approximately
ok median of odd, even and empty
ok errors
ok text
5 passed, 0 failed
test blocks#
test "freezing and boiling" {
expect_eq(to_celsius(32.0), 0.0)
A test "name" { } block is a function with no arguments that nx test runs, in file order, each in its own process state. expect(cond) and expect_eq(a, b) are builtins; a failed expectation panics with the values and the location, the test is reported as failed, and the run continues with the next one. nx run ignores test blocks entirely, so they cost the program nothing.
Inside a test, try is allowed (a test that hits an unexpected error fails with the error's name), defer works, and any function in the file is reachable, pub or not. Every module of the standard library is tested this way, in its own file: nx test std/strings.nx runs the strings module's tests.
std.testing#
The builtins cover most tests. std.testing adds the assertions that are tedious to write by hand:
expect_approx(a, b, eps)for floats.expect_err(T, r)andexpect_error(T, r, error.Name)for results that should fail.expect_contains(text, needle)andexpect_lines(actual, expected), which names the first line that differs.snapshot(name, actual): comparesactualwithsnapshots/<name>.txt, creates the file the first time, and rewrites it whenNX_UPDATE_SNAPSHOTS=1is set. Snapshot tests are how a program's whole output is kept honest with one line.
The command#
nx test file.nx # every test in the file
nx test file.nx --filter median # only the tests whose name contains it
nx test file.nx --verbose # with timings
The exit code is the number of failures, so CI sees a red run without parsing anything.
Compile-time tests#
comptime test "runs while compiling" {
expect_eq(median([5][..]) orelse 0, 5)
}
The last chapter introduced these. A comptime test runs in the interpreter during checking; it is not listed by nx test because it already ran when the file was checked, and a failure is a compile error. Use them for pure functions whose examples are worth stating next to the code, and for facts about constants.
What the test suite of this book looks like#
The programs in these chapters are tested by the same harness that tests the compiler: nx run tests/run.nx -- topo runs every one, compares its output with the recorded .expected file the page shows, runs the test blocks in the calculator and this chapter's file, builds the packages example from its own directory, renders the GUI chapter's frame, and builds this site. The harness is a Nexium program, tests/run.nx, which is worth a read once you have finished the book: it is a 400-line example of process control, threads and file handling in the language.
Next: effects, the feature that is Nexium's own.