// The examples use Ava but any test runner works (Jest, Mocha, Jasmine, etc.)import test from'ava'import multiply from'./multiply.js'import { each } from'test-each'// The code we are testing// Repeat test using different inputs and expected outputseach(
[
{ first: 2, second: 2, output: 4 },
{ first: 3, second: 3, output: 9 },
],
({ title }, { first, second, output }) => {
// Test titles will be:// should multiply | {"first": 2, "second": 2, "output": 4}// should multiply | {"first": 3, "second": 3, "output": 9}test(`should multiply | ${title}`, (t) => {
t.is(multiply(first, second), output)
})
},
)
// Snapshot testing. The `output` is automatically set on the first run,// then re-used in the next runs.each(
[
{ first: 2, second: 2 },
{ first: 3, second: 3 },
],
({ title }, { first, second }) => {
test(`should multiply outputs | ${title}`, (t) => {
t.snapshot(multiply(first, second))
})
},
)
// Cartesian product.// Run this test 4 times using every possible combination of inputseach([0.5, 10], [2.5, 5], ({ title }, first, second) => {
test(`should mix integers and floats | ${title}`, (t) => {
t.is(typeofmultiply(first, second), 'number')
})
})
// Fuzz testing. Run this test 1000 times using different numbers.each(1000, Math.random, ({ title }, index, randomNumber) => {
test(`should correctly multiply floats | ${title}`, (t) => {
t.is(multiply(randomNumber, 1), randomNumber)
})
})
Install
npm install -D test-each
This package works in both Node.js >=18.18.0 and
browsers.
import { each } from'test-each'each([{ color: 'red' }, { color: 'blue' }], ({ title }, param) => {
// Test titles will be:// should test color | {"color": "red"}// should test color | {"color": "blue"}test(`should test color | ${title}`, () => {})
})
// Plain objects can override this using a `title` propertyeach(
[
{ color: 'red', title: 'Red' },
{ color: 'blue', title: 'Blue' },
],
({ title }, param) => {
// Test titles will be:// should test color | Red// should test color | Bluetest(`should test color | ${title}`, () => {})
},
)
// The `info` argument can be used for dynamic titleseach([{ color: 'red' }, { color: 'blue' }], (info, param) => {
// Test titles will be:// should test color | 0 red// should test color | 1 bluetest(`should test color | ${info.index}${param.color}`, () => {})
})
import { each } from'test-each'// Run callback five times: a -> b -> c -> d -> eeach(['a', 'b', 'c', 'd', 'e'], (info, param) => {})
// Run callback six times: a c -> a d -> a e -> b c -> b d -> b eeach(['a', 'b'], ['c', 'd', 'e'], (info, param, otherParam) => {})
// Nested arrays are not iterated.// Run callback only twice: ['a', 'b'] -> ['c', 'd', 'e']each(
[
['a', 'b'],
['c', 'd', 'e'],
],
(info, param) => {},
)
Input functions
If a function is used instead of an array, each iteration fires it and uses
its return value instead. The function is called with the
same arguments
as the callback.
import { each } from'test-each'// Run callback with a different random number each timeeach(['red', 'green', 'blue'], Math.random, (info, color, randomNumber) => {})
// Input functions are called with the same arguments as the callbackeach(
['02', '15', '30'],
['January', 'February', 'March'],
['1980', '1981'],
(info, day, month, year) =>`${day}/${month}/${year}`,
(info, day, month, year, date) => {},
)
Fuzz testing
Integers can be used instead of arrays to multiply the number of iterations.
import faker from'faker'// Run callback 1000 times with a random UUID and color each timeeach(
1000,
faker.random.uuid,
faker.random.arrayElement(['green', 'red', 'blue']),
(info, randomUuid, randomColor) => {},
)
// `info.index` can be used as a seed for reproducible randomness.// The following series of 1000 UUIDs will remain the same across executions.each(
1000,
({ index }) => faker.seed(index) && faker.random.uuid(),
(info, randomUuid) => {},
)
import { each } from'test-each'// The `output` is automatically set on the first run,// then re-used in the next runs.each(
[
{ first: 2, second: 2 },
{ first: 3, second: 3 },
],
({ title }, { first, second }) => {
test(`should multiply outputs | ${title}`, (t) => {
t.snapshot(multiply(first, second))
})
},
)
Side effects
If callback's parameters are directly modified, they should be
copied to prevent side effects for the next iterations.
import { each } from'test-each'each(
['green', 'red', 'blue'],
[{ active: true }, { active: false }],
(info, color, param) => {
// This should not be done, as the objects are re-used in several iterations
param.active = false// But this is safe since it's a copyconst newParam = { ...param }
newParam.active = false
},
)
Iterables
iterable() can be used to iterate over each combination
instead of providing a callback.
import { iterable } from'test-each'const combinations = iterable(
['green', 'red', 'blue'],
[{ active: true }, { active: false }],
)
for (const [{ title }, color, param] of combinations) {
test(`should test color | ${title}`, () => {})
}
The return value is an
Iterable.
This can be converted to an array with the spread operator.
const array = [...combinations]
array.forEach(([{ title }, color, param]) => {
test(`should test color | ${title}`, () => {})
})
API
each(...inputs, callback)
inputs: Array | function | integer (one or several) callback: (info, ...params) => void
The npm package test-each receives a total of 800 weekly downloads. As such, test-each popularity was classified as not popular.
We found that test-each demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.It has 1 open source maintainer collaborating on the project.
Package last updated on 29 Mar 2025
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
A phishing attack targeted developers using a typosquatted npm domain (npnjs.com) to steal credentials via fake login pages - watch out for similar scams.