Introduction to Front-End Unit Testing with Jest
This guide introduces front‑end unit testing with Jest, explaining TDD and BDD concepts, installation, writing simple and ES6/TypeScript tests, configuring coverage, using common matchers, and employing mock functions, timers, and module mocking to ensure reliable, maintainable code.
Front‑end unit testing refers to testing the smallest testable units of front‑end code, such as functions, components or modules. By writing test cases for these units, developers can verify that the code behaves as expected under various conditions.
The two main development approaches mentioned are:
TDD (Test‑Driven Development): write tests before the actual code.
BDD (Behavior‑Driven Development): focus on the behavior of the system.
Benefits of unit testing include higher code quality, easier debugging, safe refactoring, and better team collaboration.
How to perform unit testing with Jest
1. Install Jest using your preferred package manager:
npm install --save-dev jest2. Create a simple demo module sum.js :
function sum(a, b) {
return a + b;
}
module.exports = sum;3. Write test cases in sum.test.js :
const sum = require('./sum');
describe('sum.js', () => {
test('adds two numbers', () => {
expect(sum(1, 2)).toBe(3);
});
it('concatenates strings', () => {
expect(sum('a', 'b')).toBe('ab');
});
});4. Add a test script to package.json :
{
"scripts": {
"test": "jest"
}
}5. Run the tests:
npm run testIf everything is set up correctly, Jest will output a summary of passed tests.
Supporting ES6 syntax
Jest runs in a Node environment using CommonJS. To use ES6 import/export , install Babel:
npm install --save-dev @babel/core @babel/preset-envCreate a .babelrc file:
{
"presets": ["@babel/preset-env"]
}Now you can write modules with ES6 syntax:
export function sum(a, b) {
return a + b;
} import { sum } from './sum';
describe('sum.js', () => {
test('adds two numbers', () => {
expect(sum(1, 2)).toBe(3);
});
test('concatenates strings', () => {
expect(sum('a', 'b')).toBe('ab');
});
});Using TypeScript
Install the TypeScript preset for Babel and the Jest type definitions:
npm install --save-dev @babel/preset-typescript npm install --save-dev @types/jestUpdate .babelrc :
{
"presets": ["@babel/preset-env", "@babel/preset-typescript"]
}Test coverage configuration
module.exports = {
collectCoverage: true,
collectCoverageFrom: ['src/utils/**/*'],
extensionsToTreatAsEsm: ['.tsx', '.jsx', '.ts'],
coverageThreshold: {
global: {
statements: 90,
functions: 90,
branches: 60
}
}
};Common matchers
Jest provides many assertion helpers:
test('equality', () => {
const foo = { bar: 1 };
expect(foo.bar).toBe(1); // passes
expect(foo).toEqual({ bar: 1 }); // passes
});
test('not', () => {
const foo = 1;
expect(foo).not.toBe(2);
});
test('match', () => {
const foo = 'hello jest';
expect(foo).toMatch(/hello/i);
});
test('contain', () => {
const data = ['foo', 'bar'];
expect(data).toContain('foo');
});Other matchers include toBeNull , toBeUndefined , toBeTruthy , toBeFalsy , toBeGreaterThan , toBeLessThanOrEqual , toBeCloseTo , etc.
Mock functions
// create a mock function
const func = jest.fn();
func.mockReturnValue(1);
func.mockReturnValueOnce(2);
func.mockImplementation(params => {
console.log('func called with', params);
});
func.mockImplementationOnce(() => {});
console.log(func.mock.calls.length); // call count
console.log(func.mock.calls[0]); // arguments of first call
console.log(func.mock.instances[0]); // this of first callMock timers
function handleData(callback) {
setTimeout(() => {
callback('bar');
setTimeout(() => {
callback('foo');
}, 1000);
}, 30 * 1000);
}
beforeEach(() => {
jest.useFakeTimers();
});
describe('test mock timer', () => {
test('runAllTimers', () => {
const fn = jest.fn();
handleData(fn);
jest.runAllTimers();
expect(fn.mock.calls.length).toBe(2);
expect(fn.mock.calls).toEqual(['bar', 'foo']);
});
});Mocking modules
// mock a module
jest.mock('./service', () => {
const actualModules = jest.requireActual('./service');
return {
...actualModules,
fetchData1: jest.fn(() => Promise.resolve({ foo: 'bar' }))
};
});
import { fetchData1, fetchData2 } from './service';
describe('test mock modules', () => {
test('fetchData1', async () => {
const res = await fetchData1();
expect(res).toEqual({ foo: 'bar' });
});
test('fetchData2', async () => {
const res = await fetchData2();
expect(res).toEqual({ type: '5G' });
});
});The article concludes with a reminder to click “The End” and a short promotional note, which is not part of the technical content.
HelloTech
Official Hello technology account, sharing tech insights and developments.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.