I 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. The test.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.