std/test¶
Assertions for writing test programs in Raven. A test is an ordinary Raven
program whose main calls these assertions: if every assertion holds, the
program runs to completion and exits zero; if one fails, it panics with a
message and aborts with a nonzero exit code.
import std/test { assert, assert_eq_int }
import std/io { println }
fun main() {
assert(1 + 1 == 2)
assert_eq_int(6 * 7, 42)
println("all passed")
}
Importing¶
The assertions are free functions, so import the ones you use with a selective list:
The test model¶
Raven has no attributes or reflection, so there is no test discovery or registration framework. A test is just a normal program. Build and run it: exit zero means every assertion held, and a nonzero exit means one failed. The panic message naming the failure goes to stderr.
A test runner is whatever invokes the compiled program and checks its exit
code (a shell loop, a CI step, or rvpm run). Because a failing assertion
aborts the whole process, the first failure stops the program; assertions
after it do not run.
Assertions¶
Each assertion checks a condition and, when it does not hold, panics with a fixed message and exits nonzero.
assert(cond: Bool)¶
Fails when cond is false. Panic message: assertion failed.
assert_msg(cond: Bool, msg: String)¶
Fails when cond is false, panicking with the caller-supplied msg. Use
this when you want the failure to explain itself.
assert_true(cond: Bool)¶
Fails when cond is false. Panic message: assertion failed: expected true.
assert_false(cond: Bool)¶
Fails when cond is true. Panic message: assertion failed: expected false.
import std/test { assert_true, assert_false }
import std/string
fun main() {
assert_true("report.csv".ends_with(".csv"))
assert_false("report.csv".ends_with(".txt"))
}
assert_eq_int(a: Int, b: Int)¶
Fails when a != b. The panic message interpolates both operands
(assertion failed: ${a} != ${b}), so a failure shows the mismatched
values.
import std/test { assert_eq_int }
fun double(x: Int) -> Int {
return x * 2
}
fun main() {
assert_eq_int(double(21), 42)
}
assert_eq_str(a: String, b: String)¶
Fails when a != b, compared by content. The panic message interpolates
both operands (assertion failed: ${a} != ${b}).
import std/test { assert_eq_str }
import std/string
fun main() {
assert_eq_str("Hello".to_upper(), "HELLO")
}
assert_eq<T: Eq + ToString>(a: T, b: T) and assert_ne<T: Eq + ToString>(a: T, b: T)¶
Generic equality and inequality over any Eq + ToString type. assert_eq
fails when a != b, assert_ne fails when a == b. Both interpolate the two
values into the failure message.
assert_eq_float(a: Float, b: Float, eps: Float)¶
Fails when a and b differ by more than eps. This is the right way to
compare floats: exact == is unreliable for computed results.
assert_some<T>(o: Option<T>) and assert_none<T>(o: Option<T>)¶
assert_some fails on None, assert_none fails on Some.
import std/test { assert_some, assert_none }
fun main() {
assert_some(Some(5))
let empty: Option<Int> = None
assert_none(empty)
}
assert_ok<T, E>(r: Result<T, E>) and assert_err<T, E>(r: Result<T, E>)¶
assert_ok fails on Err, assert_err fails on Ok.
import std/test { assert_ok, assert_err }
import std/error { Error, error }
fun parse(s: String) -> Result<Int, Error> {
return Ok(7)
}
fun main() {
assert_ok(parse("7"))
let bad: Result<Int, Error> = Err(error("bad input"))
assert_err(bad)
}
Worked example: a test program¶
A test file is a regular .rv program with a main that asserts. The
program below exercises a couple of helpers and exits zero only if every
check passes:
import std/test { assert, assert_eq_int, assert_eq_str }
import std/string
import std/io { println }
fun add(a: Int, b: Int) -> Int {
return a + b
}
fun shout(s: String) -> String {
return s.trim().to_upper()
}
fun main() {
assert_eq_int(add(2, 3), 5)
assert_eq_int(add(-1, 1), 0)
assert(add(10, 10) > 15)
assert_eq_str(shout(" hi "), "HI")
assert_eq_str(shout("raven"), "RAVEN")
println("all tests passed")
}
Run it directly:
If shout("raven") ever returned the wrong value, the program would print
the interpolated mismatch to stderr and exit nonzero instead of printing
all tests passed.
Running tests in a project¶
A test is a normal Raven program, so you run it the same way you run any other:
raven run path/to/test.rvcompiles and runs a single test file.raven build path/to/test.rvcompiles it to a binary you can invoke later (handy for CI, where the exit code is the pass/fail signal).- Inside an
rvpmproject, pointmain.rv(or a dedicated entry) at your assertions and uservpm run. See the rvpm guide for project layout and howrvpm runbuilds and executes the package entry.
To run several test files, drive them from a shell loop or CI step and check each exit code; the first nonzero exit marks a failure.
See also¶
- rvpm guide for project structure and running a package.
- std/string for the string methods used in the examples above.