Ответ
Mock и Stub — это виды тестовых заглушек (test doubles), которые заменяют реальные зависимости, но служат разным целям.
- Stub (Заглушка) — предоставляет предопределенные ответы на вызовы методов во время теста. Его цель — заставить тестируемый код работать по определенному сценарию. Stub не проверяет, как его используют.
- Mock (Макет) — это объект с предустановленными ожиданиями (expectations). Он не только возвращает данные, но и проверяет, был ли вызван определенный метод, с какими аргументами и сколько раз. Его цель — верификация взаимодействия.
Пример в PHP с PHPUnit:
// STUB: Заглушаем репозиторий, чтобы вернуть фиктивного пользователя
$userStub = $this->createStub(UserRepository::class);
$userStub->method('findById')
->willReturn(new User(1, 'John Doe'));
// Тестируемый сервис использует заглушку
$authService = new AuthService($userStub);
$result = $authService->authenticate(1); // Внутри вызовет $userStub->findById(1)
$this->assertTrue($result);
// Stub не проверял, был ли вызов findById. Он просто дал ответ.
// MOCK: Проверяем, что логгер был вызван с правильным сообщением
$loggerMock = $this->createMock(Logger::class);
// Устанавливаем ожидание: метод 'error' должен быть вызван ровно 1 раз с аргументом 'Invalid input'
$loggerMock->expects($this->once())
->method('error')
->with('Invalid input');
$validator = new Validator($loggerMock);
$validator->validate(''); // Должен залогировать ошибку
// Если метод 'error' не будет вызван или вызовется с другим аргументом, тест упадет.
Проще говоря: Stub отвечает на вопросы, а Mock следит за тем, как с ним разговаривают. В практике часто используются библиотеки вроде Mockito (Java) или unittest.mock (Python), которые создают моки, обладающие и stub-функциональностью.