Writing Your First Test¶
This walkthrough builds a small test file from scratch. Every snippet is the real thing — it is the same shape as Wisp's own test suite.
1. Require Wisp¶
2. Create a suite and add actions¶
A suite is a named group of tests. An action is one test inside it.
Suite:action(name, func) takes a human readable name and a function with no arguments
and no return value.
Suite:action returns the suite itself, which is why the calls above chain. Without
chaining you would have to keep a reference:
3. Assert something¶
Wisp.assume(value) wraps a value and returns an Assertion. Each assertion method
either passes silently or raises, so there is no return value to check.
Wisp.suite("Calculator")
:action("adds two numbers", function()
Wisp.assume(1 + 1):equalsTo(2)
end)
:action("divides", function()
Wisp.assume(10 / 2):equalsTo(5)
end)
For readability, alias assume once at the top of the file:
local Wisp = require("../src")
local assume = Wisp.assume
Wisp.suite("Calculator")
:action("adds two numbers", function()
assume(1 + 1):equalsTo(2)
end)
Wisp's own test suite does exactly this — see tests/Assertion.spec.luau.
4. Run it¶
Call the module itself. It is callable, so Wisp() runs the default runner: every
registered suite, in registration order, then prints a report.
That is the whole runner. A complete test file looks like this:
local Wisp = require("../src")
local assume = Wisp.assume
Wisp.suite("Calculator")
:action("adds two numbers", function()
assume(1 + 1):equalsTo(2)
end)
:action("coerces a numeric string", function()
assume("1" + 1):equalsTo(2)
end)
return Wisp()
Calculator
✓ adds two numbers
✓ coerces a numeric string
Suites: 1 passed out of 1 total
Tests: 2 passed out of 2 total
Took: 0.07ms
5. Make it fail¶
Assertions raise on failure, so a mismatch shows up as a failed action with the message
Wisp produced. Change equalsTo(2) to equalsTo(3):
Wisp.suite("Calculator")
:action("adds two numbers", function()
Wisp.assume(1 + 1):equalsTo(3)
end)
:action("divides", function()
Wisp.assume(10 / 2):equalsTo(5)
end)
Wisp()
Calculator
✗ adds two numbers
./run.luau:6: Assertion failed: expected 3, got 2
✓ divides
Suites: 0 passed out of 1 total
Tests: 1 passed out of 2 total
Took: 0.06ms
Note that the second action still ran. A failing assertion never stops the run — see Reading the report.
Organising multiple files¶
Wisp keeps one process-wide default runner, so a top-level file can pull in the rest and
run them in one go:
local Wisp = require("../src")
require("./Calculator.spec")
require("./Cart.spec")
require("./Inventory.spec")
return Wisp()
Each spec file simply registers suites as a side effect of being required:
local Wisp = require("../src")
local assume = Wisp.assume
Wisp.suite("Cart")
:action("starts empty", function()
assume(#{}):equalsTo(0)
end)
return true
Suite names must be unique
Registering two suites with the same name keeps only the first — the second is silently ignored. If a suite you wrote never appears in the report, check for a duplicate name elsewhere in your project.
Next¶
- Suites and Actions — naming, ordering, and lifecycle details.
- Assertions — every expectation Wisp ships with.