Testing in JavaScript: Unit, Integration and E2E Tests with Node.js

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Testing in JavaScript: Unit, Integration and E2E Tests



Testing is an integral part of developing any quality software. In the world of JavaScript (especially in the Node.js ecosystem), there are three main levels of testing: unit tests, integration tests and E2E tests (end-to-end). Each of them solves its own tasks and is used at different stages of development. In this article, we will analyze each type, show code examples and give practical recommendations.



1. Unit Tests



Unit tests check individual, isolated units of code — functions, methods or classes. The main goal is to make sure that each "unit" works correctly independently of the rest of the system. In Node.js, the Jest or Mocha frameworks with Chai are most often used for this.



Example of a unit test in Jest



Let's say we have a simple function for calculating a discount:



// discount.jsfunction calculateDiscount(price, percent) {  if (typeof price !== 'number' || typeof percent !== 'number') {    throw new Error('Both arguments must be numbers');  }  if (price < 0 || percent < 0 || percent > 100) {    throw new Error('Invalid values');  }  return price - (price * percent / 100);}

module.exports = calculateDiscount;


Test for this function:



// discount.test.jsconst calculateDiscount = require('./discount');

describe('calculateDiscount', () => { test('correctly calculates 10% discount from 1000', () => { expect(calculateDiscount(1000, 10)).toBe(900); });

test('returns 0 at 100% discount', () => { expect(calculateDiscount(500, 100)).toBe(0); });

test('throws an error with a negative price', () => { expect(() => calculateDiscount(-100, 10)).toThrow('Invalid values'); });

test('throws an error with non-numeric arguments', () => { expect(() => calculateDiscount('100', 10)).toThrow('Both arguments must be numbers'); });});


  • Pros: Fast, easy to write, precisely indicate the problematic module.
  • Cons: Do not check interaction between components.
  • When to use: For business logic functions, utilities, validators.


2. Integration Tests



Integration tests check how different modules or services work together. For example, interaction with a database, external API or file system. In Node.js, Supertest (for HTTP requests) and Jest are often used for such tests.



Example of an integration test for an Express application



Let's assume we have a simple Express server with an endpoint for creating a user:



// server.jsconst express = require('express');const app = express();app.use(express.json());

const users = [];

app.post('/users', (req, res) => { const { name, email } = req.body; if (!name || !email) { return res.status(400).json({ error: 'Name and email are required' }); } const user = { id: users.length + 1, name, email }; users.push(user); res.status(201).json(user);});

module.exports = app;


Integration test with Supertest:



// server.test.jsconst request = require('supertest');const app = require('./server');

describe('POST /users', () => { test('creates a new user with valid data', async () => { const response = await request(app) .post('/users') .send({ name: 'Ivan', email: 'ivan@example.com' });

expect(response.statusCode).toBe(201); expect(response.body).toHaveProperty('id'); expect(response.bo

Blogs

Book Recommendations