Testing in JavaScript: Unit, Integration, E2E — Complete Guide
Code quality is the key to success in any project. In the JavaScript world (especially Node.js), testing has become not just a good practice but a necessity. We break down three key levels: unit, integration, and E2E tests. You will learn how they differ, when to apply each type, and see working examples using popular tools.
1. Unit Testing: Testing Isolated Modules
Unit tests verify the operation of individual functions, classes, or modules in isolation from external dependencies (databases, APIs, file systems). This is the fastest and most reliable level.
Tools and Setup
The most popular framework is Jest. Installation:
npm install --save-dev jestAdd a script to package.json:
"scripts": { "test": "jest"}Example: Testing a Discount Calculation Function
Let's create a file discount.js:
// discount.jsfunction applyDiscount(price, discountPercent) { if (price <= 0) throw new Error('Price must be positive'); if (discountPercent < 0 || discountPercent > 100) throw new Error('Invalid discount'); return price - (price * discountPercent) / 100;}module.exports = { applyDiscount };Now let's write a unit test in discount.test.js:
const { applyDiscount } = require('./discount');
describe('applyDiscount', () => { test('calculates 10% discount correctly', () => { expect(applyDiscount(100, 10)).toBe(90); });
test('throws error for negative price', () => { expect(() => applyDiscount(-10, 10)).toThrow('Price must be positive'); });
test('throws error for invalid discount percent', () => { expect(() => applyDiscount(100, 150)).toThrow('Invalid discount'); });});Run: npm test. Jest will automatically find *.test.js files.
Tip: Use mock objects to isolate from external services.
// Example of mocking an external APIjest.mock('./api');const api = require('./api');api.getData.mockResolvedValue({ id: 1 });2. Integration Testing: Testing Component Interaction
Integration tests verify how different modules work together: database + business logic + HTTP routes. They are slower than unit tests but provide greater confidence in system correctness.
Tools: Supertest + Jest
Supertest allows you to make HTTP requests to your Express application without actually starting the server.
npm install --save-dev supertestExample: Testing a REST API
Suppose there is a simple Express application app.js:
const express = require('express');const app = express();app.use(express.json());
app.post('/users', (req, res) => { const { name, email } = req.body; if (!name || !email) { return res.status(400).json({ error: 'Missing fields' }); } // Save to DB (simplified) res.status(201).json({ id: 1, name, email });});
module.exports = app;Integration test users.test.js:
const request = require('supertest');const app = require('./app');
describe('POST /users', () => { test('creates user with valid data', async () => { const response = await request(app) .post('/users') .send({ name: 'Alice', email: 'alice@test.com' }); expect(response.status).toBe(201); expect(response.body).toHaveProperty('id'); expect(response.body.name).toBe('Alice'); });
test('returns 400 for missing email', async () => { const response = await request(app) .post('/users') .send({ name: 'Bob' });