- Reduce test code repetition
- Clearly define multiple test scenarios
- Improve test coverage with minimal code
There are two ways to use each()
:
- Inline array of test cases (like the example above)
- Tagged template literal for more readable tests
- If you pass a 1-dimensional array (like
[true, false]
or [1, 2, 3]
), .each()
calls your test with each value as its argument.
- BTW: internally, the framework temporarily transforms it to
[[1], [2], [3]]
so your callback always receives an array of arguments.
- If you pass a 2-dimensional array (like
[[1, 2, 3], [4, 5, 9]]
), each inner array is spread into the callback parameters (e.g. a=1, b=2, expected=3
).
If that isn't clear, here is an example of how to make 3 test runs, with 3 arguments for each call.
(The first test run will call with a=1, b=2, expected=3
)
test.each([
[1, 2, 3],
[4, 5, 9],
[10, 20, 30],
])(
'adds numbers %d and %d correctly',
(a, b, expected) => {
expect(a + b).toBe(expected);
}
);
You can reference the values in the test titles so each run has a unique name in your output; this makes it easier to identify which case failed.
- %d formats numbers
- %s formats strings
- %p pretty-prints objects or any value (helpful for debugging)
Here is another example of a 1D test:
test.each(['banana', 'apple'])(
'adds fruit to the list',
newFruit => {
expect(
['grapes', newFruit].join()
).toBe(`grapes,${newFruit}`);
}
);
It is often common to pass in objects in the array, like this
test.each([
{ a: 1, b: 2, expected: 3 },
{ a: -5, b: 5, expected: 0 },
{ a: 10, b: 15, expected: 25 },
])(
'adds $a + $b to equal $expected',
inputObject => {
const { a, b, expected } =
inputObject;
expect(a + b).toBe(expected);
}
);
The other syntax, which is often more useful with multiple arguments, is the tagged template literal form.
- The first line contains your headers, separated by
|
. Use identifiers that are valid JS variable names (so you can destructure them), e.g. a | b | expected
.
- Every line under that uses
${'some-value'}
to define the value
- The test function receives an object, with the values for that test run
- In your test title, you can reference them easily with
$columnName
as shown in the example below
test.each`
a | b | expected
${1} | ${2} | ${3}
${4} | ${5} | ${9}
${10} | ${20} | ${30}
`(
'adds $a + $b = $expected',
({ a, b, expected }) => {
expect(a + b).toBe(expected);
}
);
Lesson Task
The test is half written... it just needs some data passed into each(...)
.
You can pass an array of subarrays. If the first subarray was [100, 10, 90]
then it would get called with price = 100
, discountPercent = 10
, expectedResult
= 90
Pass a few example arrays like this.
Note: not shown in the test, but you could also pass an array of objects, like [{price: 100, discountPercent: 10, expectedResult}]
and change your test function to accept a single argument.