Reduce unit tests boilerplate with Jest’s .each syntax
When writing unit tests, especially in JavaScript/TypeScript with Jest, you often run into a common problem: repetition.
Imagine testing a function with several input-output pairs. The tests can become bloated and harder to read.
This is where Jest’s .each
syntax shines. It lets you write cleaner, data-driven tests with minimal duplication.
The Problem: Repetitive Test Cases
Take a simple sum
function:
function sum(a, b) {
return a + b;
}
Without .each
, you might write your tests like this:
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
test('adds 2 + 3 to equal 5', () => {
expect(sum(2, 3)).toBe(5);
});
test('adds -1 + -1 to equal -2', () => {
expect(sum(-1, -1)).toBe(-2);
});
These tests work, but they are verbose. You repeat the same logic over and over with only the inputs and expected results changing.
The Solution: Jest’s .each
Syntax
Jest’s .each
allows you to define test cases as data and reuse the same test body.
Here is the same example using .each
:
describe('sum', () => {
test.each([
[1, 2, 3],
[2, 3, 5],
[-1, -1, -2],
])('returns %i when %i + %i', (a, b, expected) => {
expect(sum(a, b)).toBe(expected);
});
});
This single block of code replaces three separate test cases.
Each array in the .each
list corresponds to a test run, and Jest automatically substitutes the values.
Bonus: Named Arguments with Tagged Template Literals
You can also use named arguments for clarity:
test.each`
a | b | expected
${1} | ${2} | ${3}
${2} | ${3} | ${5}
${-1}| ${-1}| ${-2}
`('returns $expected when $a + $b', ({ a, b, expected }) => {
expect(sum(a, b)).toBe(expected);
});
This syntax is more readable, especially when dealing with longer or more descriptive variable names.
It reads like a mini table of test cases.
Why Use .each
?
- Less boilerplate: Define the test once and reuse it.
- Better readability: Data-driven tests are easier to scan.
- Easier maintenance: Add or remove cases without duplicating test logic.
- Fewer mistakes: Repeating the same code invites copy-paste errors.
Use Case: Validating Multiple Inputs
Suppose you are testing a validation function like isEmail
. You can define all test cases in one place:
test.each([
['user@example.com', true],
['not-an-email', false],
['hello@world.io', true],
['@missing.local', false],
])('validates %s as %s', (input, expected) => {
expect(isEmail(input)).toBe(expected);
});
This approach scales better than writing individual test blocks for every email address.
Conclusion
Jest’s .each
is a powerful way to reduce duplication in your test suite.
It helps you write cleaner, more maintainable, and more expressive tests.
Next time you find yourself writing nearly identical test cases, reach for .each
—your future self will thank you.