Skip to content

llbbl/lisunit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lisunit

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.

Why a library?

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.

Install

Lisette does not have a package manager yet, so installation is copy-paste:

  1. Copy the src/lisunit/ directory from this repo into your project's src/ directory.
  2. 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.

Quickstart

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.

API

Suite

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_all

Run multiple suites in order with a combined summary.

lisunit.run_all(suites: Slice<Suite>)

Assertions

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(())
})

Running the self-demo

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 run

Expected output: 8 green checks across two suites.

Design notes

  • 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 return Result, so ? propagates the first failure to the runner, which catches it via match to isolate failures between tests.
  • Test isolation via match. The runner's per-case loop uses a match (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 lisunit hard-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's recover() if/when Lisette exposes it.
  • No main-free test discovery. Because there's no lis test command, your main() must call the runner. A dedicated test-only binary isn't possible today.

Roadmap

Likely short-term additions:

  • More assertions: assert_contains, assert_empty, assert_ok / assert_err on Result
  • 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.

Contributing

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.

License

MIT — see LICENSE.

About

A small unit-testing library for the Lisette programming language, in the spirit of JUnit / pytest.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors