Skip to content

Introduction

Wisp is a test framework for Luau, the scripting language used by Roblox. You group tests into named suites, you assert things inside those tests, and you get a small printed summary at the end.

local Wisp = require("wisp")

Wisp.suite("Cart")
  :action("starts empty", function()
    Wisp.assume(#cart.items):equalsTo(0)
  end)

Wisp()

The mental model

There are only three moving parts.

1. The module. You require Wisp once. The returned value is both a table and a function, so it can be both configured and invoked:

local Wisp = require("wisp")

Wisp.suite("Math") -- configure
Wisp()             -- run

2. Suites and actions. A suite is a named bucket of actions; an action is a single named test. Suite:action returns the suite, so calls chain:

Wisp.suite("Math")
  :action("adds", function() ... end)
  :action("subtracts", function() ... end)

3. Assertions. Wisp.assume(value) wraps a value and hands back an Assertion whose methods describe what you expect. An assertion fails by raising a Lua error, which Wisp catches and reports as a failed test.

Wisp.assume(2 + 2):equalsTo(4)
Wisp.assume(name):isType("string")
Wisp.assume(compute()):notThrows()

Design goals

Goal How Wisp meets it
Readable output The report is printed as plain text — no colours, no JSON, no reporter plugin.
Cheap to run A few hundred lines, no dependency tree. Startup cost is a single require.
Hard to misuse Every method is either satisfied or raises. Nothing returns a boolean you might forget to check.
Typed The source is --!strict and exports its types for your editor or LSP.

What Wisp does not do

Being small is a feature, but it also means some things are absent. Knowing the edges up front will save you time.

  • No async / hooks. There is no beforeEach, afterEach, before, after, or promise support. Each action is a plain () -> () and is expected to finish on its own.
  • No .only / .skip. Every registered suite and action always runs.
  • No exit code control. A failing run still exits 0. See Reading the report if you need a non-zero status.
  • No custom reporters. The report format is fixed; see Runner if you want to drive suites yourself.
  • One default runner. All of Wisp.suite and Wisp() share a single process-wide runner. Suite names must therefore be unique.

Requirements

  • Luau (or Lune) that supports string interpolation with backticks and utf8.char. Wisp's own source relies on these, and your test files will naturally use them too.
  • Strict mode is supported but not required. Wisp ships --!strict source and a types.d.luau module so tooling can infer Suite and Assertion for you.

Next

Install it with Installation, then write a test in Your First Test.