Today I am going to show you how to prevent code duplication when writing unit tests with Jest .each
.
Let's get started!
- Know how to write test cases in Jest
- Have basic TypeScript knowledge
- Understand template literal syntax of JavaScript
Given a string transformer function to convert a string to a kebab cased one
https://gist.github.com/phatnguyenuit/5ada24a985010faae611357b96bc4af8
const createTransform = (
pattern: string | RegExp,
separator: string,
wordTransformer: (word: string) => string,
) => (text: string) => text.split(pattern).map(wordTransformer).join(separator);
const exhaustedPattern = /[\s_-]|(?=[A-Z0-9])/;
export const toKebabCase = createTransform(exhaustedPattern, '-', (word) =>
word.toLowerCase(),
);
// "my_example" => "this-is-my-example"
// "anotherExample123" => "another-example-1-2-3"
And how your test cases are organized
https://gist.github.com/phatnguyenuit/b5eecb0e1d0dff158600134d29aa3ab8
import { toKebabCase } from './utils';
describe('toKebabCase', () => {
it('should work from empty string', () => {
expect(toKebabCase('')).toEqual('');
});
it('should return "my-example" when given "my_example"', () => {
expect(toKebabCase('my_example')).toEqual('my-example');
});
it('should return "another-example" when given "Another-Example"', () => {
expect(toKebabCase('Another-Example')).toEqual('another-example');
});
it('should return "another-example" when given "anotherExample"', () => {
expect(toKebabCase('anotherExample')).toEqual('another-example');
});
it('should return "another-example-1-2-3" when given "anotherExample123"', () => {
expect(toKebabCase('anotherExample123')).toEqual('another-example-1-2-3');
});
});
So you can see there are 5 test cases for toKebabCase
utilities. Is there any way to make it better ? shorter ? and easier to maintain ?
✅ YES, you can refactor the above test cases by using Jest it.each
it
is an alias fortest
. So do not get confused when sometimes you seetest
instead ofit
. Refer to this. I prefer usingit
more thantest
because of the shortest name
it.each
signature
// it.each`table`(name, fn, timeout)
it.each`
var1 | var2 | expectedResult
${1} | ${2} | ${3}
${2} | ${3} | ${5}
`('should work', ({ var1, var2, expectedResult }) => {
expect(yourFn(var1, var2)).toEqual(expectedResult);
});
The table
here is tagged literal string in JavaScript. Why does it call table ?
Because we organize our test cases like a table.
var1 | var2 | expectedResult |
---|---|---|
1 | 2 | 3 |
2 | 3 | 5 |
- The 1st row contains variable name columns separated by
|
- From the 2nd row, each row contains variable value columns we pass to the test case as template literal expression
${value}
, separated by|
also.
Okay for now, you can refactor the above test cases to reduce code duplication.
https://gist.github.com/phatnguyenuit/851c2742294dee460fb38056caa0da23
import { toKebabCase } from './utils';
describe('toKebabCase', () => {
it.each`
text | expectedResult
${''} | ${''}
${'my_example'} | ${'my-example'}
${'Another-Example'} | ${'another-example'}
${'anotherExample'} | ${'another-example'}
${'anotherExample123'} | ${'another-example-1-2-3'}
`(
'should return "$expectedResult" when given "$text"',
({ text, expectedResult }) => {
expect(toKebabCase(text)).toEqual(expectedResult);
},
);
});
Wow, do you surprise that our test cases shorter, easier to understand and maintain
Wait, wait. Does it work ? Yes, absolutely YES ✔.
All test cases are passed now. You can see the GREEN
✅color (passed test cases), it can make your happy day at work 😁. Avoid the RED
❌ one (failed test cases) as little as possible if you want to get a long day 😂🤣
You might wonder if the describe.each
exists?
✅YES, there is describe.each
supported by Jest.
For describe.each
, it's similar to it.each
but for grouping test cases into collections.
You can explore more by going through the references section.
It is very cool to use it.each
from Jest to make your test cases maintainable
, avoid duplication
.
Thank you for reading my article! I hope it is helpful for you. If you have any concerns or questions, do not hesitate to leave a comment below.
See you in the next article!