Writing Tests¶
The #[skuld::test] attribute¶
Every test function must be annotated with #[skuld::test]. The attribute registers the function with the harness and supports the following options:
#[skuld::test] // no options
#[skuld::test(requires = [valgrind, my_binary])] // runtime preconditions
#[skuld::test(name = "custom display name")] // custom name in output
#[skuld::test(labels = [DOCKER, SLOW])] // labels for filtering
#[skuld::test(ignore)] // statically ignored
#[skuld::test(ignore = "blocked on #123")] // ignored with reason
#[skuld::test(serial)] // serial with everything
#[skuld::test(serial = DATABASE & !FAST)] // serial with a filter
Options can be combined:
#[skuld::test(requires = [docker], labels = [INTEGRATION], serial)]
fn heavy_test() { /* ... */ }
Note
Do not add #[test] alongside #[skuld::test] — the skuld macro already registers the function. Adding both will produce a compile error.
Preconditions¶
Each entry in requires = [...] must be a function with signature fn() -> Result<(), String>:
fn docker() -> Result<(), String> {
use std::process::{Command, Stdio};
Command::new("docker")
.arg("--version")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.is_ok_and(|s| s.success())
.then_some(())
.ok_or_else(|| "docker not installed".into())
}
fn corpus_exists() -> Result<(), String> {
std::path::Path::new("test_data/corpus")
.exists()
.then_some(())
.ok_or_else(|| "test_data/corpus not found".into())
}
#[skuld::test(requires = [docker, corpus_exists])]
fn integration_test() {
// Runs only if both checks pass.
}
If any requirement returns Err, the test is marked ignored (not failed) and the reason is collected for the unavailability summary.
Fixture requirements also propagate: if a test uses a fixture that has requires = [...], those requirements are checked too. See Fixtures for details.
Custom display names¶
By default, the test name in output is the function name. Override it with name:
#[skuld::test(name = "arithmetic: 2 + 2 = 4")]
fn test_add() {
assert_eq!(2 + 2, 4);
}
Static ignore¶
Mark a test as statically ignored (always skipped, no precondition check):
#[skuld::test(ignore)]
fn work_in_progress() { /* ... */ }
#[skuld::test(ignore = "blocked on #123")]
fn blocked_test() { /* ... */ }
Statically ignored tests do not appear in the unavailability summary.
Output¶
When all requirements are met:
running 3 tests
test smoke_test ... ok
test full_pipeline ... ok
test unit_test ... ok
test result: ok. 3 passed; 0 failed; 0 ignored
When requirements are missing:
running 3 tests
test smoke_test ... ignored
test full_pipeline ... ignored
test unit_test ... ok
test result: ok. 1 passed; 0 failed; 2 ignored
--- Unavailable (2) ---
smoke_test: valgrind not installed
full_pipeline: valgrind not installed