Is there a reason I would prefer one method over the other? Here are some examples:
describe('some tests', () => {
[1, 2, 3].forEach(num => {
test(`${num}: test`, () => {
doSomeTestStuff(num)
})
})
// vs.
test.each([1, 2, 3])('%s: test', (num) => {
doSomeTestStuff(num)
})
})
It seems kind of difficult to read the test.each
syntax, especially when you can just do native javascript iteration to achieve what seems to be the same effect. Teardown/setup still happens the same way (from what I can tell).
Is there a reason I would prefer one method over the other? Here are some examples:
describe('some tests', () => {
[1, 2, 3].forEach(num => {
test(`${num}: test`, () => {
doSomeTestStuff(num)
})
})
// vs.
test.each([1, 2, 3])('%s: test', (num) => {
doSomeTestStuff(num)
})
})
It seems kind of difficult to read the test.each
syntax, especially when you can just do native javascript iteration to achieve what seems to be the same effect. Teardown/setup still happens the same way (from what I can tell).
3 Answers
Reset to default 9I would prefer the vanilla JS forEach
version, because it's just JavaScript. This means:
- You don't need to be familiar with Jest specifically, you don't need to go looking in the docs for what exactly
test.each
does. Thetest.each`table`
form particularly can behave in a way that's surprising, see e.g. Jest test.each with literal table: "Not enough arguments supplied for given headings". - Equally it's portable between test frameworks, you can use the same approach in Mocha, Tap, etc.
- You get proper IDE support - you need to wait for runtime to find out whether
'.add($a, $b)'
actually matches the properties in the objects, whereas`.add(${a}, ${b})`
can be checked and autocompleted statically (and manipulated however you like, as you can interpolate arbitrary expressions).
It's not true that there's any difference in the way the tests are executed or reported. The forEach
loop runs at test discovery time, and all three tests are registered. Then they're all run, separately, at execution time. You'd only see problems with failures impacting other cases if you moved the loop inside a single test case, e.g.:
describe('some bad tests', () => {
// this an example of how not to do it
test('num tests', () => {
[1, 2, 3].forEach(num => {
doSomeTestStuff(num)
})
})
})
One other thing to note with the vanilla forEach
approach is that it is common, as you've shown, to inline the arrays of test cases. If you're relying on ASI, as your examples suggest you are, this sometimes means you need to prefix the array with ;
:
describe('some tests', () => {
[1, 2, 3].forEach(num => {
test(`${num}: test`, () => {
doSomeTestStuff(num)
})
})
;[4, 5, 6].forEach(num => {
test(`${num}: test`, () => {
doSomeTestStuff(num)
})
})
})
Without this, you'd get TypeError: Cannot read property '6' of undefined
.
This is maybe a vote in Jest's favour, but if you're not going to use semicolons you do need to know when they will and won't get inserted - I'd argue it's a vote in semicolons' favour.
Great question!
On the surface it would seem like the two are basically equivalent, but there are a few reasons why you may want to use .each
instead.
- If an expectation in a
forEach
fails, it'll fail immediately and stop execution. This leaves you in the dark on whether the other tests in theforEach
passed or failed. With.each
it is basically writing a separate and distinct test for each item in your array. - If an expectation in a
forEach
fails, it's not always obvious which item in the array caused the failure. In a.each
you automatically get context from the unique test name. - With
.each
you can use tagged template literals to handle multiple parameters easily, and as parameters grow the.each
can be very expressive and much more readable than an array with arbitrary values. - Since, as I said before,
.each
creates a separate test for each item, the final report will make it seem like you wrote more tests than you actually have. Just a small thing that brings satisfaction when you look at the final output.
One advantage of test.each
can be better tool support. I just noticed this with the VS Code plugin orta.vscode-jest.