Einleitung: Warum Tests ohne Mocks wie Kaffee ohne Koffein sind
\n\nStellen Sie sich vor: Sie schreiben eine TypeScript-Anwendung, die E-Mails versendet, mit einer Datenbank kommuniziert und auf eine externe API zugreift. Sie führen die Tests aus – und plötzlich fällt alles aus, weil der Mailserver vorübergehend nicht erreichbar ist. Kommt Ihnen das bekannt vor?
\n\nHier kommen Mock-Objekte (Stubs) und Fixtures (Testdaten) ins Spiel. Sie ermöglichen es, den Code von der Außenwelt zu isolieren und Tests schnell, zuverlässig und vorhersagbar zu machen. In diesem Artikel auf Deutsch analysieren wir anhand realer Beispiele, wie man Mock in TypeScript richtig einsetzt, welche Fixtures für TypeScript es gibt und wie man Fehler vermeidet.
\n\nDer Artikel wurde für PythonLib geschrieben – ein Bildungsportal, auf dem wir lehren, qualitativ hochwertigen Code zu schreiben. Los geht's!
\n\nWas sind Mock-Objekte und Fixtures?
\n\nMock-Objekte: Schauspieler-Doubles in Ihrem Code
\n\nMock-Objekt (oder einfach Mock) ist ein Ersatz für ein reales Objekt, das dessen Verhalten imitiert. Sie sagen: "Wenn die Methode sendEmail() aufgerufen wird, gib true zurück". Und der Test greift nicht auf die echte Mail zu.
\n\nIn TypeScript werden Mocks meist mit den Bibliotheken Jest oder Vitest erstellt. Sie ermöglichen zu überprüfen, ob eine Methode aufgerufen wurde, mit welchen Argumenten und wie oft.
\n\nFixtures: Vorbereitete Daten für Tests
\n\nFixtures sind vorbereitete Datensätze. Zum Beispiel ein Benutzerobjekt, eine Bestellliste oder eine Anwendungskonfiguration. Anstatt sie jedes Mal manuell zu erstellen, speichern Sie Fixtures in separaten Dateien.
\n\nDie Kombination Mock + Fixture ist der Standard für TypeScript-Tests im Jahr 2024.
\n\nPraxis: Tests mit Mocks und Fixtures schreiben
\n\nUmgebungseinrichtung
\n\nFür die Beispiele verwenden wir Jest (obwohl alles auch mit Vitest funktioniert). Stellen Sie sicher, dass die Abhängigkeiten installiert sind:
\n\nnpm install --save-dev jest @types/jest ts-jest\n\nFügen Sie in jest.config.js hinzu:
module.exports = {\n preset: 'ts-jest',\n testEnvironment: 'node',\n};\n\nBeispiel 1: Mock des E-Mail-Versanddienstes
\n\nAngenommen, wir haben einen Benachrichtigungsdienst:
\n\n// notificationService.ts\nexport class EmailService {\n async sendEmail(to: string, subject: string, body: string): Promise<boolean> {\n // Echter Versand – den lassen wir unberührt\n return true;\n }\n}\n\nexport class NotificationManager {\n constructor(private emailService: EmailService) {}\n\n async notifyUser(email: string, message: string): Promise<string> {\n const result = await this.emailService.sendEmail(email, 'Benachrichtigung', message);\n return result ? 'Gesendet' : 'Fehler';\n }\n}\n\nJetzt schreiben wir einen Test mit einem Mock-Objekt:
\n\n// notificationService.test.ts\nimport { NotificationManager, EmailService } from './notificationService';\n\ndescribe('NotificationManager', () => {\n it('sollte eine Benachrichtigung senden und "Gesendet" zurückgeben', async () => {\n // Mock erstellen\n const mockEmailService: jest.Mocked<EmailService> = {\n sendEmail: jest.fn().mockResolvedValue(true),\n };\n\n const manager = new NotificationManager(mockEmailService);\n const result = await manager.notifyUser('test@test.com', 'Hallo!');\n\n expect(result).toBe('Gesendet');\n expect(mockEmailService.sendEmail).toHaveBeenCalledWith(\n 'test@test.com',\n 'Benachrichtigung',\n 'Hallo!'\n );\n });\n\n it('sollte "Fehler" zurückgeben, wenn der Versand fehlschlägt', async () => {\n const mockEmailService: jest.Mocked<EmailService> = {\n sendEmail: jest.fn().mockResolvedValue(false),\n };\n\n const manager = new NotificationManager(mockEmailService);