Fortgeschrittenes Testen in TypeScript: von Unit bis E2E
Testen ist nicht nur die Überprüfung der Code-Funktionalität, sondern das Fundament professioneller Entwicklung in TypeScript. Auf fortgeschrittenem Niveau wechseln wir von einfachen Assert-Statements zum Aufbau isolierter, schneller und zuverlässiger Testpyramiden. In diesem Artikel behandeln wir praktische Ansätze für Unit-, Integrations- und E2E-Tests unter Verwendung der besten Tools des TypeScript-Ökosystems.
1. Fortgeschrittenes Unit-Testen mit Jest und TypeScript
Unit-Tests prüfen isolierte Module. In TypeScript liegt die Hauptherausforderung im Umgang mit Abhängigkeiten, Typen und Asynchronität.
1.1. Mocks und Typisierung mit jest.mocked
Verwenden Sie jest.mocked für die vollständige Typisierung von Mocks. Dies stellt sicher, dass Sie keine nicht existierende Methode aufrufen.
import { fetchUserData } from './api';import { processUser } from './userProcessor';
jest.mock('./api');
const mockedFetchUserData = jest.mocked(fetchUserData);
describe('processUser', () => { it('sollte die Benutzerdaten verarbeiten', async () => { const mockUser = { id: 1, name: 'Alice', email: 'alice@test.com' }; mockedFetchUserData.mockResolvedValue(mockUser);
const result = await processUser(1);
expect(result).toEqual({ displayName: 'Alice', contact: 'alice@test.com' }); expect(mockedFetchUserData).toHaveBeenCalledWith(1); });
it('sollte einen Fehler werfen, wenn keine E-Mail vorhanden ist', async () => { mockedFetchUserData.mockResolvedValue({ id: 2, name: 'Bob', email: null }); await expect(processUser(2)).rejects.toThrow('Email is required'); });});1.2. Testen komplexer Typen und Generics
Erstellen Sie für Funktionen mit Generics Test-Hilfstypen, um Duplikate zu vermeiden.
// Codefunction getFirstElement<T>(arr: T[]): T | undefined { return arr[0];}
// Testtype TestArray = number[];const array: TestArray = [10, 20, 30];
describe('getFirstElement', () => { it('sollte das erste Zahlenelement zurückgeben', () => { const result = getFirstElement(array); expect(result).toBe(10); // TypeScript leitet automatisch den Typ number ab });
it('sollte undefined für ein leeres Array zurückgeben', () => { const result = getFirstElement([]); expect(result).toBeUndefined(); });});2. Integrationstests: Datenbank und API
Integrationstests prüfen das Zusammenspiel von Modulen: Controller, Services und Datenbank. Für TypeScript-Anwendungen auf Node.js ist die Kombination aus Jest + Supertest + Testcontainers ideal.
2.1. Einrichtung einer Testdatenbank mit Testcontainers
Testcontainers startet PostgreSQL in Docker und gewährleistet so eine saubere Umgebung für jeden Test.
import { PostgreSqlContainer, StartedPostgreSqlContainer } from '@testcontainers/postgresql';import { Client } from 'pg';
let container: StartedPostgreSqlContainer;let client: Client;
beforeAll(async () => { container = await new PostgreSqlContainer().start(); client = new Client({ connectionString: container.getConnectionUri() }); await client.connect(); await client.query(` CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) UNIQUE NOT NULL ); `);}, 30000); // Timeout 30 Sekunden zum Herunterladen des Images
afterAll(async () => { await client.end(); await container.stop();});
describe('User Repository', () => { it('sollte einen Benutzer erstellen und zurückgeben', async () => { const res = await client.query( `INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *`, ['Test', 'test@example.com'] ); expect(res.rows[0].name).toBe('Test'); });});2.2. Testen der REST-API mit Supertest
Starten Sie die Express-Anwendung und test