storybakery/tiniest
Minimal, portable testing libraries for Luau.
tiniest is a collection of minimal, portable testing libraries for Luau,
built on a few principles:
- Simple, idiomatic Luau throughout.
- Zero external dependencies.
- Small core, extend with plugins.
- Runtime independence.
- No opinions about project structure.
tiniest comes with pre-configured bundles for popular runtimes like Roblox and
Lune, or pick and mix your own modules and plugins!
Features
test()anddescribe()callbacks- Clean, correctly-cropped stack traces
- Declarative
expect()API with line numbers and code visualisation - Pretty-formatted summaries of reports with emoji, Unicode, and colour
- Benchmark how long tests run for
- Extensible, lightweight plugin system
- Snapshot testing for all quotable data types
Planned
- Test discovery for Lune
Installation
No installation needed!
tiniest is distributed as a set of portable Luau files that sit next to each
other. Drop them into your lib folder, keep the ones you need, and start using
tiniest right away :)
pesde
tiniest can also be installed from the pesde index:
pesde add storybakery/tiniest
For maintainers, the publish flow is:
pesde auth login
# 1) Lune target
# [target]
# environment = "lune"
# lib = "src/tiniest_for_lune.luau"
pesde publish --dry-run
pesde publish
# 2) Luau target (same name + same version)
# [target]
# environment = "luau"
# lib = "src/tiniest.luau"
pesde publish --dry-run
pesde publish
# 3) Roblox target (same name + same version)
# [target]
# environment = "roblox"
# lib = "src/tiniest_for_roblox.luau"
# build_files = ["src"]
pesde publish --dry-run
pesde publish
As of February 25, 2026, pesde x jiwonz/multitarget currently fails in this
setup with could not resolve child component "GreenTea", so this repository
uses the sequential target publish flow above instead.
Usage
๐จ The printed reports come with colour - try it in your terminal!
Here's an example file written with tiniest_for_lune:
--!strict
local tiniest = require("@tiniest_for_lune").configure({
snapshot_path = "./test/__snapshots__",
save_snapshots = true
})
local function my_test_suite()
local describe = tiniest.describe
local test = tiniest.test
local expect = tiniest.expect
local snapshot = tiniest.snapshot
describe("some cool features", function()
test("it works", function()
expect(2 + 2).is(4)
end)
test("snapshots", function()
snapshot({
hello = "world",
foo = true,
bar = 2
})
end)
end)
end
local tests = tiniest.collect_tests(my_test_suite)
tiniest.run_tests(tests, {})
The above example generates a report like this and prints it to stdout:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Status of 2 test(s) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
some cool features โธ it works - 74.9ยตs
โ
some cool features โธ snapshots - 137.71ms
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 2 passed, 0 failed โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Time to run: 217.9ms
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Failures look like this:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Errors from 2 test(s) โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ some cool features โธ it works
Expectation not met
โ
16 โ expect(4).is(5)
โ
[string "test/test_main"]:16
โ some cool features โธ snapshots
Snapshot does not match
โ
78 โ snapshot({
โ ["bar"] = 2;
โ ["foo"] = true;
โ ["hello"] = "world";
โ })
โ
โ -- snapshot on disk:
โ snapshot({
โ ["bar"] = 5;
โ ["foo"] = false;
โ ["hello"] = "earth";
โ })
โ
[string "test/test_main"]:20
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Status of 2 test(s) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ some cool features โธ it works - 111.6ms
โ some cool features โธ snapshots - 174.9ms
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 0 passed, 2 failed โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Time to run: 292.81ms
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
License
Licensed the same way as all of my open source projects: BSD 3-Clause + Security Disclaimer.
As with all other projects, you accept responsibility for choosing and using this project.
See LICENSE or the license summary for details.