Mocking & Stubbing: Test-Doubles erklärt | Enjyn Gruppe
Hallo Welt
Hallo Welt
Original Lingva Deutsch
Übersetzung wird vorbereitet...
Dieser Vorgang kann bis zu 60 Sekunden dauern.
Diese Seite wird erstmalig übersetzt und dann für alle Besucher gespeichert.
0%
DE Zurück zu Deutsch
Übersetzung durch Lingva Translate

235 Dokumentationen verfügbar

Wissensdatenbank

Mocking Stubbing Testing

Zuletzt aktualisiert: 20.01.2026 um 10:03 Uhr

Mocking & Stubbing: Test-Doubles erklärt

Test-Doubles isolieren Code für besseres Testing. Lernen Sie den Unterschied zwischen Mocks, Stubs und Spies.

Die Test-Double Typen

Typ Beschreibung Verwendung
Stub Gibt vordefinierte Werte zurück Input für Code unter Test
Mock Prüft Erwartungen (Aufrufe) Verifizieren von Interaktionen
Spy Echte Implementierung + Tracking Aufrufe aufzeichnen
Fake Vereinfachte Implementierung In-Memory DB statt echter DB
Dummy Platzhalter, wird nicht verwendet Erfüllt Signatur

Stubs: Kontrollierte Rückgabewerte

// Jest (JavaScript)
const userRepository = {
    findById: jest.fn()
};

// Stub: Immer gleichen Wert zurückgeben
userRepository.findById.mockReturnValue({ id: 1, name: 'Max' });

// Stub: Promise zurückgeben
userRepository.findById.mockResolvedValue({ id: 1, name: 'Max' });

// Stub: Unterschiedliche Werte pro Aufruf
userRepository.findById
    .mockReturnValueOnce({ id: 1, name: 'Max' })
    .mockReturnValueOnce({ id: 2, name: 'Anna' })
    .mockReturnValue(null); // Für alle weiteren

// Stub: Basierend auf Argumenten
userRepository.findById.mockImplementation((id) => {
    if (id === 1) return { id: 1, name: 'Max' };
    if (id === 2) return { id: 2, name: 'Anna' };
    return null;
});

Mocks: Erwartungen prüfen

// Service der getestet wird
class NotificationService {
    constructor(emailClient) {
        this.emailClient = emailClient;
    }

    async notifyUser(user, message) {
        await this.emailClient.send(user.email, message);
    }
}

// Test mit Mock
describe('NotificationService', () => {
    test('sends email to user', async () => {
        // Mock erstellen
        const emailClient = {
            send: jest.fn().mockResolvedValue(true)
        };

        const service = new NotificationService(emailClient);
        const user = { email: 'test@example.com' };

        // Act
        await service.notifyUser(user, 'Hello!');

        // Assert: Mock-Erwartungen prüfen
        expect(emailClient.send).toHaveBeenCalled();
        expect(emailClient.send).toHaveBeenCalledTimes(1);
        expect(emailClient.send).toHaveBeenCalledWith('test@example.com', 'Hello!');
    });

    test('does not send if user has no email', async () => {
        const emailClient = { send: jest.fn() };
        const service = new NotificationService(emailClient);

        await service.notifyUser({ email: null }, 'Hello!');

        expect(emailClient.send).not.toHaveBeenCalled();
    });
});

Spies: Echte Implementierung tracken

// Spy auf echte Methode
const calculator = {
    add(a, b) {
        return a + b;
    }
};

const addSpy = jest.spyOn(calculator, 'add');

// Echte Implementierung wird ausgeführt
const result = calculator.add(2, 3); // = 5

// Aber wir können Aufrufe prüfen
expect(addSpy).toHaveBeenCalledWith(2, 3);

// Spy wieder entfernen
addSpy.mockRestore();

// Spy mit überschriebener Implementierung
jest.spyOn(calculator, 'add').mockReturnValue(100);
calculator.add(2, 3); // = 100

Module Mocking

// axios mocken
jest.mock('axios');
import axios from 'axios';

test('fetches users', async () => {
    axios.get.mockResolvedValue({
        data: [{ id: 1, name: 'Max' }]
    });

    const result = await fetchUsers();

    expect(axios.get).toHaveBeenCalledWith('/api/users');
    expect(result).toHaveLength(1);
});

// Partial Mock (nur bestimmte Funktionen)
jest.mock('./utils', () => ({
    ...jest.requireActual('./utils'),
    fetchData: jest.fn()
}));

// Auto-Mock zurücksetzen
beforeEach(() => {
    jest.clearAllMocks(); // Aufrufe zurücksetzen
    jest.resetAllMocks(); // + Implementierung zurücksetzen
});

PHP: Mocking mit PHPUnit

<?php
use PHPUnit\Framework\TestCase;

class OrderServiceTest extends TestCase
{
    public function test_creates_order_and_sends_notification(): void
    {
        // Mock erstellen
        $notificationService = $this->createMock(NotificationService::class);

        // Erwartung definieren
        $notificationService
            ->expects($this->once())
            ->method('send')
            ->with(
                $this->equalTo('user@example.com'),
                $this->stringContains('Order')
            );

        // Service unter Test
        $orderService = new OrderService($notificationService);
        $orderService->createOrder(['email' => 'user@example.com']);
    }

    public function test_returns_stubbed_value(): void
    {
        $repository = $this->createMock(UserRepository::class);

        // Stub: Rückgabewert definieren
        $repository
            ->method('findById')
            ->willReturn(new User(1, 'Max'));

        $service = new UserService($repository);
        $user = $service->getUser(1);

        $this->assertEquals('Max', $user->getName());
    }

    public function test_stub_with_callback(): void
    {
        $repository = $this->createMock(UserRepository::class);

        $repository
            ->method('findById')
            ->willReturnCallback(function ($id) {
                return $id === 1 ? new User(1, 'Max') : null;
            });
    }
}

Fakes: Vereinfachte Implementierungen

// Fake In-Memory Repository
class FakeUserRepository {
    constructor() {
        this.users = new Map();
        this.nextId = 1;
    }

    async create(data) {
        const user = { id: this.nextId++, ...data };
        this.users.set(user.id, user);
        return user;
    }

    async findById(id) {
        return this.users.get(id) || null;
    }

    async findAll() {
        return Array.from(this.users.values());
    }

    async delete(id) {
        return this.users.delete(id);
    }
}

// Im Test verwenden
test('user workflow', async () => {
    const repo = new FakeUserRepository();
    const service = new UserService(repo);

    const user = await service.createUser({ name: 'Max' });
    expect(user.id).toBe(1);

    const found = await service.getUser(1);
    expect(found.name).toBe('Max');
});

Best Practices

✅ Gutes Mocking:
  • Mocke externe Dependencies (APIs, DBs, File System)
  • Mocke nicht den Code der getestet wird
  • Vermeide zu viele Mocks in einem Test
  • Prüfe nur relevante Interaktionen
❌ Anti-Patterns:
  • Jede Methode mocken (Test ist zu gekoppelt)
  • Implementierungsdetails testen statt Verhalten
  • Mocks nicht zurücksetzen zwischen Tests

Weitere Informationen

Enjix Beta

Enjyn AI Agent

Hallo 👋 Ich bin Enjix — wie kann ich dir helfen?
120