A small unit-testing library for the Lisette programming language, in the spirit of JUnit / PHPUnit / pytest.
Status: experimental. Tested against lisette 0.1.2.
Lisette doesn't yet ship a built-in test runner (it's on the roadmap), and the language has no macros or attributes today for test discovery. lisunit takes the library-based approach — you register test cases as closures on a Suite and call run().
When Lisette eventually ships a core lis test command, the runner/discovery parts of this library will likely become obsolete — but the assertion and matcher helpers should remain useful on top of it.
Lisette does not have a package manager yet, so installation is copy-paste:
- Copy the
src/lisunit/directory from this repo into your project'ssrc/directory. - Add
import "lisunit"in files that need it.
That's it. When Lisette gets a package manager, this README will be updated with the proper dependency declaration.
import "lisunit"
fn add(a: int, b: int) -> int {
a + b
}
fn main() {
lisunit.Suite.new("math")
.case("add produces sum", || {
lisunit.assert_eq_int(add(2, 3), 5)?
Ok(())
})
.case("add is commutative", || {
lisunit.assert_eq_int(add(2, 3), add(3, 2))?
Ok(())
})
.run()
}
Running with lis run will produce colored output and exit non-zero if any tests fail.
Builder for a named collection of test cases.
lisunit.Suite.new(name: string) -> Suite
.case(name: string, body: fn() -> Result<(), string>) -> Suite
.run() // prints results, exits non-zero on failure
.run_silent() -> SuiteResult // returns result without printing (for programmatic use)
Run multiple suites in order with a combined summary.
lisunit.run_all(suites: Slice<Suite>)
All assertions return Result<(), string> so they compose with the ? operator.
lisunit.assert_true(cond: bool, msg: string) -> Result<(), string>
lisunit.assert_false(cond: bool, msg: string) -> Result<(), string>
lisunit.assert_eq_int(actual: int, expected: int) -> Result<(), string>
lisunit.assert_eq_string(actual: string, expected: string) -> Result<(), string>
lisunit.assert_eq_bool(actual: bool, expected: bool) -> Result<(), string>
lisunit.fail(msg: string) -> Result<(), string>
A test case body returns Result<(), string>. Return Ok(()) to pass, Err(msg) to fail — or just use ? on assertions and let them propagate:
.case("a complex test", || {
let result = do_something()
lisunit.assert_true(result.is_valid(), "should be valid")?
lisunit.assert_eq_int(result.count, 3)?
Ok(())
})
This repo is itself a runnable Lisette project that demonstrates the library testing a few trivial functions:
git clone https://github.com/llbbl/lisunit
cd lisunit
lis runExpected output: 8 green checks across two suites.
- Manual registration. Lisette has no
#[test]attribute or reflection; every test case is registered explicitly via.case(...). This is closer to JUnit/Jest/PHPUnit than to Rust/Go's built-in test runners. Result<(), string>as the test return type. Asserts returnResult, so?propagates the first failure to the runner, which catches it viamatchto isolate failures between tests.- Test isolation via
match. The runner's per-case loop uses amatch(not?) so one failing test does not stop subsequent tests in the suite. - Colored output via ANSI escapes. Go's stdlib has no color helper, so
lisunithard-codes a small set of ANSI codes. If your terminal doesn't support them, they'll render as literal escape sequences. - No panic recovery yet. A panic inside a test body (e.g. an unwrap of
None) will currently kill the whole suite. Future versions may use Go'srecover()if/when Lisette exposes it. - No
main-free test discovery. Because there's nolis testcommand, yourmain()must call the runner. A dedicated test-only binary isn't possible today.
Likely short-term additions:
- More assertions:
assert_contains,assert_empty,assert_ok/assert_erronResult - Setup / teardown hooks (
before_each,after_each) - Name filtering:
lis run -- --filter "arithmetic"runs only matching cases - Per-test progress output (print each PASS/FAIL as it happens)
- Panic recovery once the mechanism is clear
When Lisette's core lis test ships, lisunit will pivot to being a higher-level matcher/helper library layered on top, similar to how Hamcrest layers on JUnit.
Issues and PRs welcome. Please file suggestions for assertions you'd like to see, or design thoughts for when this library and a future core lis test command should meet.
MIT — see LICENSE.