Persue more permutations.
Cover all corners.
Scan every scenario.
Comb each combination.
Demand more dimensions.
Master the matrix.
busted-matrix helps you write test cases for different permutations of your
inputs.
Example:
numbers_spec.lua
MATRIX {
vars = {
odd = { 1, 7, 13 },
even = { 4, 42 },
},
tags = {
{ match = { odd = 7 }, tags = { "lucky" } },
{ match = { odd = 13 }, tags = { "unlucky" } },
},
}
local function is_odd(n)
return n % 2 ~= 0
end
local function is_even(n)
return n % 2 == 0
end
local sum = 0
setup(function()
for _, each in ipairs(matrix.all) do
sum = sum + each.odd + each.even
end
end)
describe("numbers", function()
assert.equals(180, sum, "setup() adds everything up")
it("odds are odd", function()
assert(is_odd(matrix.odd))
end)
it("evens are even", function()
assert(is_even(matrix.even))
end)
it("odd times even is always even", function()
assert(is_even(matrix.odd * matrix.even))
end)
end)$ busted -o TAP --helper busted.matrix.helper examples/numbers_spec.lua
ok 1 - numbers [even = 4, odd = 1] odds are odd
ok 2 - numbers [even = 4, odd = 1] evens are even
ok 3 - numbers [even = 4, odd = 1] odd times even is always even
ok 4 - numbers #lucky [even = 4] odds are odd
ok 5 - numbers #lucky [even = 4] evens are even
ok 6 - numbers #lucky [even = 4] odd times even is always even
ok 7 - numbers #unlucky [even = 4] odds are odd
ok 8 - numbers #unlucky [even = 4] evens are even
ok 9 - numbers #unlucky [even = 4] odd times even is always even
ok 10 - numbers [even = 42, odd = 1] odds are odd
ok 11 - numbers [even = 42, odd = 1] evens are even
ok 12 - numbers [even = 42, odd = 1] odd times even is always even
ok 13 - numbers #lucky [even = 42] odds are odd
ok 14 - numbers #lucky [even = 42] evens are even
ok 15 - numbers #lucky [even = 42] odd times even is always even
ok 16 - numbers #unlucky [even = 42] odds are odd
ok 17 - numbers #unlucky [even = 42] evens are even
ok 18 - numbers #unlucky [even = 42] odd times even is always even
1..18Without busted-matrix, permutation testing is ungainly, typically requiring you
to iterate over inputs and create describe() and it() blocks in the loop
body:
- Each new variable increases code indentation level
- Ugly string-munging code is required to create unique test labels
local odds = { 1, 7, 13 }
local evens = { 4, 42 }
for _, odd in ipairs(odds) do
for _, even in pairs(evens) do
describe("numbers [even = " .. tostring(even)
.. ", odd = " .. tostring(odd) .. "]", function()
it("odds are odd", function()
assert(is_odd(odd))
end)
it("evens are even", function()
assert(is_even(even))
end)
it("odd times even is always even", function()
assert(is_even(odd * even))
end)
end)
end
endluarocks install busted-matrixbusted-matrix requires busted >= 2.1.2
To use busted-matrix's main features, you must tell busted to load it as a
helper script. This can be done via the command line:
# hard-coded path
busted --helper path/to/busted/matrix.helper
# require()-like syntax
busted -- helper "busted.matrix.helper"...or via your .busted config file:
return {
_all = {
helper = "busted.matrix.helper",
-- alternatively
helper = "path/to/busted/matrix/helper.lua",
},
}Once you've installed busted-matrix and configured busted to load it as a
helper, you can start using the MATRIX block in your test files.
MATRIX is overloaded, so there are several ways to use it depending on what
suits your code. The following examples are equivalent:
-- all at once
MATRIX {
vars = {
x = { 1, 2 },
y = { 3, 4 },
},
}
-- add vars individually
MATRIX("x", { 1, 2 })
MATRIX("y", { 3, 4 })
-- run your own code in the MATRIX context
MATRIX(function()
add("x", { 1, 2 })
add("y", { 3, 4 })
end)Matrices can be expanded and updated via include directives:
MATRIX(function()
add("x", { 1, 2 })
add("y", { 3, 4 })
-- add a combo with `x = 23`
include { x = 23 }
-- add `n = 98` to all combos
include { n = 98 }
-- override `n = 99` when `x == 1`
include { n = 99, x = 1 }
-- add `z = 12` when `y == 4`
include { z = 12, y = 4 }
end)
describe("matrix", function()
describe("(z)", function()
it("is 12 when y is 4 and nil otherwise", function()
-- XXX: conditional asserts are often a test code smell. This is a
-- demonstration of matrix behavior and not a suggestion of how to
-- write tests
if matrix.y == 4 then
assert.equals(12, matrix.z)
else
assert.is_nil(matrix.z)
end
end)
end)
describe("(n)", function()
it("is 99 when x is 1, nil when x is 23, and 98 otherwise", function()
if matrix.x == 1 then
assert.equals(99, matrix.n)
elseif matrix.x == 23 then
assert.is_nil(matrix.n)
else
assert.equals(98, matrix.n)
end
end)
end)
describe("x = 23", function()
it("everything else is nil", function()
if matrix.x == 23 then
assert.is_nil(matrix.y)
assert.is_nil(matrix.n)
assert.is_nil(matrix.z)
end
end)
end)
end)Combinations can be excluded from the matrix via the exclude directive. This
pattern is typically used when some combination of input variables is untestable
or fundamentally unreachable in real world code:
MATRIX {
vars = {
meat = { true, false },
pudding = { true, false },
},
exclude = {
-- How can you have any pudding if you don't eat yer meat?!
{ meat = false, pudding = true },
},
}For the sake of familiarity with existing tooling and workflows, matrix behavior
is modeled after the matrix property in Github Actions (busted-matrix even
has unit tests for Github's usage examples). See the
documentation
for more.
busted-matrix can tag test cases based on configurable combination criteria.
This is helpful for applying tags to your tests for filtering:
MATRIX {
vars = {
foo = { true, false },
bar = { true, false },
},
tags = {
-- we can't test foo in CI yet!
-- add the `skip_ci` tag so we can skip it for now
{ match = { foo = true }, tags = { "foo", "skip_ci" } },
},
}
describe("foo?", function()
it("test", function()
-- ...
end)
end)Tagging has broader effects on the way that busted-matrix labels your test
cases. When a tag matches a matrix combination, the fields that it matched on
are consumed and no longer used in the label:
$ busted -o TAP --helper busted.matrix.helper foo_spec.lua
ok 1 - foo? #foo #skip_ci [bar = true] test
ok 2 - foo? #foo #skip_ci [bar = false] test
ok 3 - foo? [bar = true, foo = false] test
ok 4 - foo? [bar = false, foo = false] test
1..4busted-matrix will print diagnostic info to stdout when debugging is
enabled. This can be done a few different ways:
- set
BUSTED_MATRIX_DEBUG=1in busted's environment - pass the
--debugflag to the helper (busted -Xhelper --debug ...) - run busted in verbose mode (
busted --verbose ...)
Use -Xhelper --no-debug to explicitly disable debug output from
busted-matrix (i.e. busted --verbose -Xhelper --no-debug ... enables
busted's verbose mode and disables busted-matrix's debug mode)