Unit Testing with Jest
Testing Node.js applications with Jest
Challenge
Section titled “Challenge”Write comprehensive unit tests and maintain high code coverage.
Explanation
Section titled “Explanation”Unit tests verify individual functions and components work correctly in isolation.
Jest Setup
Section titled “Jest Setup”npm install --save-dev jestnpm init -y{ "jest": { "testEnvironment": "node", "coverage": { "threshold": { "global": { "lines": 80 } } } }}Basic Tests
Section titled “Basic Tests”function add(a, b) { return a + b;}
function subtract(a, b) { return a - b;}
module.exports = { add, subtract };
// math.test.jsconst { add, subtract } = require('./math');
describe('Math operations', () => { test('add function', () => { expect(add(2, 3)).toBe(5); });
test('subtract function', () => { expect(subtract(5, 3)).toBe(2); });});Assertions
Section titled “Assertions”test('various assertions', () => { // Equality expect(value).toBe(5); // Strict equality expect(value).toEqual(obj); // Deep equality
// Truthiness expect(value).toBeTruthy(); expect(value).toBeFalsy(); expect(value).toBeNull(); expect(value).toBeUndefined();
// Numbers expect(4).toBeGreaterThan(3); expect(3).toBeLessThan(4); expect(3.2).toBeCloseTo(3.2);
// Strings expect('hello').toMatch(/hell/); expect('hello').toContain('ll');
// Arrays expect([1, 2, 3]).toContain(2); expect([1, 2, 3]).toHaveLength(3);
// Exceptions expect(() => { throw new Error('error'); }).toThrow('error');});Setup and Teardown
Section titled “Setup and Teardown”describe('Database', () => { let db;
beforeAll(async () => { db = await connectDatabase(); });
beforeEach(() => { // Clear data before each test db.clear(); });
afterEach(() => { // Cleanup after each test });
afterAll(async () => { await db.disconnect(); });
test('query data', async () => { const result = await db.query('SELECT * FROM users'); expect(result).toEqual([]); });});Mocking
Section titled “Mocking”// Mock entire modulejest.mock('request');const request = require('request');
test('fetch data', () => { request.mockResolvedValue({ status: 200, body: {} });
expect(request).toHaveBeenCalled(); expect(request).toHaveBeenCalledWith('/api/data');});
// Mock specific functionconst fetchUser = jest.fn((id) => { return Promise.resolve({ id, name: 'John' });});
test('fetch user', async () => { const user = await fetchUser(1);
expect(fetchUser).toHaveBeenCalledWith(1); expect(user).toEqual({ id: 1, name: 'John' }); expect(fetchUser).toHaveBeenCalledTimes(1);});
// Mock return valuesconst getValue = jest.fn() .mockReturnValueOnce(1) .mockReturnValueOnce(2) .mockReturnValue(3);
expect(getValue()).toBe(1);expect(getValue()).toBe(2);expect(getValue()).toBe(3);Async Testing
Section titled “Async Testing”test('async function', async () => { const result = await fetchData(); expect(result).toEqual({ data: 'value' });});
test('promise', () => { return fetchData().then(result => { expect(result).toEqual({ data: 'value' }); });});
test('resolved value', () => { expect.assertions(1); return expect(fetchData()).resolves.toEqual({ data: 'value' });});
test('rejected value', () => { expect.assertions(1); return expect(failingFunction()).rejects.toThrow('error');});Snapshots
Section titled “Snapshots”test('user object', () => { const user = { id: 1, name: 'John', email: 'john@example.com' };
expect(user).toMatchSnapshot(); // Saves snapshot on first run // Compares on subsequent runs});Coverage
Section titled “Coverage”npm test -- --coveragePASS math.test.js-----------|----------|----------|----------|File | Lines | Statements| Functions-----------|----------|----------|----------|math.js | 90% | 90% | 100%Best Practices
Section titled “Best Practices”// 1. Descriptive test names// ❌ test('add', () => {})// ✅ test('add should return sum of two numbers', () => {})
// 2. One assertion per test (usually)test('add function', () => { expect(add(2, 3)).toBe(5);});
// 3. Arrange, Act, Asserttest('calculate total', () => { // Arrange const items = [10, 20, 30];
// Act const total = items.reduce((a, b) => a + b, 0);
// Assert expect(total).toBe(60);});
// 4. Use beforeEach for setupbeforeEach(() => { // Setup common test data});
// 5. Test error casestest('divide by zero throws error', () => { expect(() => divide(10, 0)).toThrow();});