Testing¶
(kaappi test) — test framework with groups, assertions, and reporting.
Pure Scheme — no dependencies. SRFI-64 inspired.
Quick start¶
(import (kaappi test))
(test-group "arithmetic"
(test-equal "2+2" 4 (+ 2 2))
(test-assert "positive" (> 5 0)))
(test-group "errors"
(test-error "type error" (lambda () (+ 1 "two"))))
(test-exit)
Run it:
Output:
test-exit prints the summary and exits with code 1 if any test failed.
Assertions¶
test-equal — deep structural equality¶
Uses equal? to compare. Works with lists, vectors, strings, numbers:
(test-equal "lists match" '(1 2 3) (list 1 2 3))
(test-equal "strings" "hello" (string-append "hel" "lo"))
(test-equal "vectors" #(1 2) (vector 1 2))
(test-equal "nested" '((a . 1)) (list (cons 'a 1)))
On failure, shows expected and actual values:
test-eqv — value identity¶
Uses eqv?. Best for numbers, characters, and booleans:
(test-eqv "exact number" 42 (* 6 7))
(test-eqv "character" #\a (string-ref "abc" 0))
(test-eqv "boolean" #t (pair? '(1)))
test-assert — truthiness¶
Passes if the expression is any non-#f value:
(test-assert "positive" (> 5 0))
(test-assert "string check" (string? "hello"))
(test-assert "list is pair" (pair? '(1 2 3)))
test-not — falsiness¶
Passes if the expression is #f:
test-approximate — floating point tolerance¶
Passes if |expected - actual| <= tolerance:
test-error — expected errors¶
Passes if the thunk raises an error:
(test-error "division by zero" (lambda () (/ 1 0)))
(test-error "type error" (lambda () (+ 1 "two")))
(test-error "out of range" (lambda () (vector-ref #(1 2) 5)))
The thunk must be a zero-argument lambda — it is called inside a guard
that catches the error.
Test groups¶
Groups organize tests into labeled sections. They nest:
(test-group "parser"
(test-group "numbers"
(test-equal "integer" 42 (parse-int "42"))
(test-equal "negative" -7 (parse-int "-7")))
(test-group "strings"
(test-equal "simple" "hello" (parse-str "\"hello\""))
(test-equal "empty" "" (parse-str "\"\""))))
Output:
Controlling output¶
Suppress passing tests¶
For large suites, show only failures and the summary:
Get test counts¶
(test-pass-count) ;=> number of passed tests
(test-fail-count) ;=> number of failed tests
(test-skip-count) ;=> number of skipped tests
Testing a library¶
Typical project layout:
tests/test-parser.scm:
(import (scheme base)
(kaappi test)
(my-lib parser))
(test-group "parser"
(test-equal "parse integer" 42 (parse "42"))
(test-error "bad input" (lambda () (parse "???"))))
(test-exit)
Run from the project root:
Setup and teardown¶
Use let bindings inside groups for per-group setup:
(import (kaappi test) (kaappi sqlite))
(test-group "database"
(let ((db (sqlite-open ":memory:")))
(sqlite-exec db "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
(test-equal "insert"
1 (sqlite-exec db "INSERT INTO users (name) VALUES (?)" "Alice"))
(test-equal "query"
'#(1 "Alice")
(car (sqlite-query db "SELECT * FROM users")))
(sqlite-close db)))
Checking procedure coverage¶
After writing tests, verify all exported procedures are exercised:
Uncalled procedures are listed by name. For CI, generate Cobertura XML:
Running in CI¶
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Kaappi
run: curl -fsSL https://kaappi-lang.org/install.sh | bash
- name: Install dependencies
run: thottam install kaappi-test
- name: Run tests
run: kaappi tests/test-all.scm
Running multiple test files¶
Create a runner that loads all test files:
;; tests/test-all.scm
(import (kaappi test))
(load "tests/test-parser.scm")
(load "tests/test-validator.scm")
(test-exit)
Or aggregate in the shell:
API reference¶
Groups¶
| Procedure | Description |
|---|---|
(test-group name body ...) |
Labeled test group |
(test-begin name) |
Start group (low-level) |
(test-end) |
End group (low-level) |
Assertions¶
| Procedure | Description |
|---|---|
(test-equal name expected actual) |
Deep equality (equal?) |
(test-eqv name expected actual) |
Value identity (eqv?) |
(test-assert name expr) |
Truthy check |
(test-not name expr) |
Falsy check |
(test-approximate name expected actual tol) |
Within tolerance |
(test-error name thunk) |
Expects error |
Reporting¶
| Procedure | Description |
|---|---|
(test-exit) |
Print summary, exit 1 on failure |
(test-verbose! bool) |
Show/hide passing tests |
(test-pass-count) |
Number of passed tests |
(test-fail-count) |
Number of failed tests |
(test-skip-count) |
Number of skipped tests |