Testing in TypeScript: Unit, Integration and E2E Tests

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

Introduction: Why Test TypeScript Code?



TypeScript is a superset of JavaScript that adds static typing. However, types do not guarantee correct application logic. Testing in TypeScript is not just a "good practice" but a necessity for maintaining code quality, preventing regressions, and accelerating development.



In this article, we will cover three main levels of testing: unit, integration, and e2e (end-to-end). You will learn which tools to use, see code examples, and understand how to build a balanced testing strategy for your TypeScript project.



1. Unit Testing in TypeScript



What are Unit Tests?

Unit tests check isolated modules or functions. They run quickly and do not require external dependencies (databases, network, file system). The main goal is to ensure that every small unit of code works correctly.



Tools: Jest + ts-jest

The most popular framework for unit testing in TypeScript is Jest with the ts-jest transformer. It supports mocks, spies, coverage, and integrates well with TypeScript.



Unit Test Example

Suppose we have a function for calculating a discount:



// discount.tsexport function calculateDiscount(price: number, discountPercent: number): number {  if (price < 0 || discountPercent < 0 || discountPercent > 100) {    throw new Error('Invalid input');  }  return price * (1 - discountPercent / 100);}


Writing the test:



// discount.test.tsimport { calculateDiscount } from './discount';

describe('calculateDiscount', () => { it('should apply 10% discount correctly', () => { expect(calculateDiscount(1000, 10)).toBe(900); });

it('should throw error for negative price', () => { expect(() => calculateDiscount(-100, 10)).toThrow('Invalid input'); });

it('should handle 0% discount', () => { expect(calculateDiscount(500, 0)).toBe(500); });});


Run with: npx jest. Jest will automatically find all *.test.ts files.



Best Practices for Unit Tests

  • Isolate dependencies — use mocks (jest.mock) for modules that access the database or API.
  • Test edge cases — empty arrays, null, negative numbers.
  • Follow the FIRST principle — Fast, Isolated, Repeatable, Self-validating, Timely.
  • Use describe/it — for a readable test structure.


2. Integration Testing



What do Integration Tests Check?

Integration tests check the interaction of several modules: controller + service + database, or API endpoint + middleware. They are slower than unit tests but provide more confidence in the correct coupling of components.



Tools: Supertest + Jest + in-memory DB

For testing Express/Node.js APIs, Supertest (HTTP assertions) is often used together with Jest. The database can be run in-memory (e.g., mongodb-memory-server or sqlite3).



Integration Test Example for Express API

Suppose we have a simple REST API for managing users:



// app.tsimport express from 'express';import { userRouter } from './routes/userRoutes';

const app = express();app.use(express.json());app.use('/api/users', userRouter);

export { app };


Test with Supertest:



// user.integration.test.tsimport request from 'supertest';import { app } from '../app';

describe('POST /api/users', () => { it('should create a new user and return 201', async () => {

Blogs

Book Recommendations