Testing
Built-in test support with #[test], assert_eq, and assert. No external test framework needed.
Writing tests
Annotate any function with #[test] to make it a test case. Test functions take no arguments and return nothing. Use assert_eq(actual, expected) to check values and assert(condition) to check boolean conditions.
#[test]
fn test_addition() {
assert_eq(2 + 2, 4);
}
#[test]
fn test_string_interpolation() {
let name = "Hew";
assert_eq(f"Hello, {name}!", "Hello, Hew!");
}
#[test]
fn test_condition() {
let x = 42;
assert(x > 0);
}Running tests
Run all tests in a file with hew test. The test runner discovers every #[test] function, runs them, and reports pass/fail results.
# Run all tests in a file
hew test my_tests.hew
# Run all test files in a directory
hew test tests/Testing actors
Actors are tested the same way as regular code: spawn them, send messages, and assert results. Use await to get responses from receive fn handlers and sleep_ms() to wait for fire-and-forget messages to be processed.
actor Counter {
let count: Int;
receive fn increment() {
count += 1;
}
receive fn get_count() -> Int {
count
}
}
#[test]
fn test_counter_increment() {
let c = spawn Counter(count: 0);
c.increment();
c.increment();
c.increment();
sleep_ms(100);
let count = await c.get_count();
assert_eq(count, 3);
}
#[test]
fn test_counter_add() {
let c = spawn Counter(count: 0);
c.increment();
c.increment();
sleep_ms(100);
let count = await c.get_count();
assert_eq(count, 2);
}Testing actor-to-actor communication
Test multi-actor patterns by spawning the full topology and checking the final state. Workers, collectors, and forwarders can all be exercised in tests.
actor Collector {
let total: Int;
receive fn report(value: Int) {
total += value;
}
receive fn get_total() -> Int {
total
}
}
actor Worker {
let id: Int;
let collector: Collector;
receive fn compute(n: Int) {
collector.report(n * n);
}
}
#[test]
fn test_fan_in_pattern() {
let collector = spawn Collector(total: 0);
let w1 = spawn Worker(id: 1, collector: collector);
let w2 = spawn Worker(id: 2, collector: collector);
let w3 = spawn Worker(id: 3, collector: collector);
w1.compute(2); // 4
w2.compute(3); // 9
w3.compute(4); // 16
sleep_ms(200);
let total = await collector.get_total();
assert_eq(total, 29);
}Testing select and join
select and join expressions work naturally in test functions. Test timeout behaviour with after clauses and parallel results with join.
actor FastActor {
let value: Int;
receive fn get() -> Int { value }
}
actor SlowActor {
let value: Int;
receive fn get() -> Int {
sleep_ms(200);
value
}
}
#[test]
fn test_select_picks_fast() {
let fast = spawn FastActor(value: 42);
let slow = spawn SlowActor(value: 999);
let result = select {
x from await fast.get() => x,
y from await slow.get() => y,
};
assert_eq(result, 42);
}
#[test]
fn test_select_timeout() {
let slow = spawn SlowActor(value: 123);
let result = select {
x from await slow.get() => x,
after 50 => -1,
};
assert_eq(result, -1);
}
#[test]
fn test_join_waits_for_all() {
let w1 = spawn FastActor(value: 10);
let w2 = spawn FastActor(value: 20);
let w3 = spawn FastActor(value: 30);
let results = join {
await w1.get(),
await w2.get(),
await w3.get(),
};
assert_eq(results.0 + results.1 + results.2, 60);
}Testing pure functions
Pure functions and algorithms are straightforward to test. Define the function alongside its tests in the same file, or import it from another module.
fn binary_search(arr: Vec<Int>, target: Int) -> Int {
var low = 0;
var high = arr.len();
while low < high {
let mid = low + (high - low) / 2;
let val = arr.get(mid);
if val == target { return mid; }
if val < target { low = mid + 1; }
else { high = mid; }
}
return -1;
}
#[test]
fn test_binary_search_found() {
let arr: Vec<Int> = Vec::new();
arr.push(1);
arr.push(3);
arr.push(5);
arr.push(7);
arr.push(9);
assert_eq(binary_search(arr, 7), 3);
}
#[test]
fn test_binary_search_not_found() {
let arr: Vec<Int> = Vec::new();
arr.push(1);
arr.push(3);
arr.push(5);
assert_eq(binary_search(arr, 4), -1);
}
#[test]
fn test_binary_search_empty() {
let arr: Vec<Int> = Vec::new();
assert_eq(binary_search(arr, 1), -1);
}Testing enums and pattern matching
Use assert_eq with match to verify enum behaviour and Option/Result handling.
enum Color { Red; Green; Blue; }
fn color_code(c: Color) -> Int {
match c {
Red => 0,
Green => 1,
Blue => 2,
}
}
#[test]
fn test_enum_variants() {
assert_eq(color_code(Red), 0);
assert_eq(color_code(Green), 1);
assert_eq(color_code(Blue), 2);
}
fn safe_divide(a: Int, b: Int) -> Result<Int, String> {
if b == 0 { Err("division by zero") }
else { Ok(a / b) }
}
#[test]
fn test_result_ok() {
let r = safe_divide(10, 2);
let val = match r {
Ok(v) => v,
Err(e) => -1,
};
assert_eq(val, 5);
}
#[test]
fn test_result_err() {
let r = safe_divide(10, 0);
let is_err = match r {
Ok(v) => false,
Err(e) => true,
};
assert(is_err);
}