Что такое моки и стабы в тестировании? В чём разница?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Моки и стабы в тестировании: основные концепции и различия
В контексте тестирования, особенно unit-тестирования (модульного тестирования), моки (mock objects) и стабы (stub objects) являются разновидностями тестовых дублёров (test doubles). Они используются для замены реальных зависимостей тестируемого объекта (например, внешних сервисов, сложных классов, баз данных) с целью создания контролируемого и изолированного тестового окружения. Основная цель — проверить поведение самого тестируемого модуля, а не его зависимостей.
Определение и роль стабов (Stubs)
Стабы — это объекты, которые предоставляют заранее заданные, фиксированные ответы на вызовы методов во время теста. Их главная задача — убрать зависимость от внешнего поведения и обеспечить тестируемый код необходимыми данными "как будто" из реальной системы.
- Функция: Замена реального объекта для предоставления предопределённых ответов.
- Когда использовать: Когда нужно просто "протолкнуть" тестовые данные через систему, чтобы проверить реакцию тестируемого модуля на эти данные.
- Пример сценария: Тестирование сервиса, который зависит от
UserRepository. СтабUserRepositoryвсегда возвращает заранее созданного пользователя с определённым ID, независимо от того, существует он в реальной БД или нет. - Философия: "Не важно, что происходит снаружи; вот данные, которые ты получишь. Проверяй своё поведение на них."
Пример кода стаба в PHP (используя PHPUnit):
// Стаб для репозитория пользователей
$userRepositoryStub = $this->createStub(UserRepository::class);
$userRepositoryStub->method('findById')
->willReturn(new User(1, 'John Doe'));
// Тестируемый сервис использует стаб вместо реального репозитория
$userService = new UserService($userRepositoryStub);
$result = $userService->getUserProfile(1);
$this->assertEquals('John Doe', $result->getName());
Определение и роль моков (Mocks)
Моки — это объекты, которые не только предоставляют фиксированные ответы (как стабы), но также позволяют устанавливать ожидания (expectations) относительно того, как они будут взаимодействовать с тестируемым кодом. Тест с моком проверяет, что тестируемый модуль вызвал методы мока ожидаемым образом (определённое количество раз, с конкретными аргументами).
- Функция: Замена реального объекта для проверки взаимодействия между тестируемым объектом и зависимостью.
- Когда использовать: Когда нужно убедиться, что тестируемый модуль правильно использует свою зависимость (например, вызывает метод сохранения в БД именно один раз).
- Пример сценария: Тестирование сервиса отправки уведомлений. Мок
EmailSenderпроверяет, что методsend()был вызван точно один раз с конкретным адресом электронной почты. - Философия: "Я не только дам тебе нужный ответ, но и буду следить, как ты меня используешь. Если ты нарушишь ожидания — тест провалится."
Пример кода мока в PHP (используя PHPUnit):
// Мок для сервиса отправки email
$emailSenderMock = $this->createMock(EmailSender::class);
// Устанавливаем ожидание: метод send должен быть вызван один раз с конкретным аргументом
$emailSenderMock->expects($this->once())
->method('send')
->with('client@example.com');
// Тестируемый сервис использует мок
$notificationService = new NotificationService($emailSenderMock);
$notificationService->sendWelcomeEmail('client@example.com');
// Проверка ожиданий выполняется автоматически PHPUnit после вызова метода
Ключевые различия между моками и стабами
- Цель использования:
* **Стаб**: Подмена зависимости для предоставления *данных* (состояния). Проверяется *реакция* тестируемого объекта на эти данные.
* **Мок**: Подмена зависимости для проверки *взаимодействия* (поведения). Проверяется *способ использования* тестируемого объекта этой зависимости.
- Фокус проверки:
* **Стаб**: Фокус на **состояние** (state verification). Мы проверяем, что после выполнения операции результат (выходные данные, состояние объекта) соответствует ожиданиям.
* **Мок**: Фокус на **поведение** (behavior verification). Мы проверяем, что были произведены ожидаемые вызовы методов зависимого объекта.
- Настройка в тесте:
* **Стаб**: Настраивается для возврата конкретных значений. Обычно не проверяет, как его методы были вызваны.
* **Мок**: Настраивается с ожиданиями (`expects()`). Тест провалится, если эти ожидания не будут выполнены (например, метод не был вызван или был вызван с неправильными аргументами).
Практический совет и заключение
В современных фреймворках для тестирования (таких как PHPUnit) граница между моками и стабами часто размыта технически — один и тот же метод (createMock() или createStub()) может быть настроен как для простой подмены (стаб), так и для проверки взаимодействия (мок). Однако концептуальное различие остаётся критически важным для написания чистых и понятных тестов.
- Используйте стаб, когда ваш тест сосредоточен на том, что делает система (на её конечном результате или состоянии).
- Используйте мок, когда ваш тест должен проверить, как система делает что-то (какие именно вызовы она совершает к внешним компонентам).
Понимание этой разницы помогает избежать чрезмерного использования моков там, где достаточно стаба, что ведет к более надежным и менее хрупким тестам.