Tools
Testing
A test in Burn is a program: it passes when it exits with 0 and fails when it stops with a runtime error, a failed
assert or a non-zero exit code. Tests live in the tests/ folder of a project, one program per file, and
burn test runs each of them in its own process.
colors/
├── burn.toml
├── src/lib.bn
└── tests/
├── convert.bn
└── parse_errors.bnA first test
import "std/testing"
import "github.com/ada/colors"
assert(toHex(Color { r: 255, g: 0, b: 0 }) == "#ff0000", "red")
assert(toHex(Color { r: 0, g: 0, b: 0 }) == "#000000", "black")
print("all tests passed")assert(condition, message) stops the program with runtime error: assertion failed: <message> when the condition is
false. A test imports the project's own package by its name, like any other code that uses it.
burn test # every file in tests/
burn test convert # only the files whose name contains "convert"
ash test # the same, through ashtesting github.com/ada/colors (2 files)
test tests/convert.bn ... ok
test tests/parse_errors.bn ... FAILED
---- tests/parse_errors.bn ----
runtime error: assertion failed: empty input is rejected
--> tests/parse_errors.bn:7:1
|
7 | assert(parse("") == null, "empty input is rejected")
| ^
1 passed, 1 failedburn test exits with 1 when a test fails, so it can gate a CI job. In a workspace
it runs the tests of every member, and -p <member> picks one.
Many checks in one file
std/testing also groups checks into named tests that keep going after a failure and report at the end:
import "std/testing"
fun slug(title: string): string {
return lower(replace(trim(title), " ", "-"))
}
test("spaces become dashes", fun() {
expectEqual(slug("Hello World"), "hello-world")
})
test("the ends are trimmed", fun() {
expectEqual(slug(" Burn "), "burn")
})
test("numbers", fun() {
expectNear(0.1 + 0.2, 0.3, 0.000001)
expectNotEqual(slug("a b"), "a b")
})
finish()ok spaces become dashes
ok the ends are trimmed
ok numbers
3 passed, 0 failed| Function | Checks |
|---|---|
assert(condition, message) |
stops the program right away when condition is false |
test(name, body) |
runs body, then prints ok or FAIL with the name |
expect(condition, message) |
condition is true |
expectEqual(actual, expected) |
the two are equal with ==; the message shows both |
expectNotEqual(actual, unwanted) |
the two differ |
expectNear(actual, expected, tolerance) |
two floats are at most tolerance apart |
finish() |
prints the totals and exits with 1 if any test failed |
The expect functions record a failure in the current test instead of stopping, so one run shows every broken
check. Always end the file with finish(), otherwise a failed expect does not change the exit code.
Testing errors and output
A test can check that code reports a failure through its result:
import "std/fs"
import "std/testing"
test("reading a missing file", fun() {
expect(!fileExists("does/not/exist.txt"), "the file should be missing")
expect(!Files.isDir("does/not"), "the folder should be missing")
})
finish()To check what a program prints, or that it stops with a runtime error, run it as a separate process with
Process.output from std/process and compare its out, err and code.
Tests on every backend
A test is an ordinary program, so it can run on each backend:
burn test # on bvm
burnc tests/convert.bn -o /tmp/t && /tmp/t # natively
burnc tests/convert.bn --target js -o t.js && node t.jsIn CI
- run: burn fmt --check src/*.bn tests/*.bn
- run: burn check
- run: burn testburn check type-checks every source, test and example without running them, and burn fmt --check fails when a file
is not formatted. See Burn standards for how to name and organize tests.