Усі три замінюють справжню залежність у тесті, але перевіряють різне.
- Fake - робоча спрощена реалізація.
Mail::fake()справді приймає листи, а тест перевіряє результат: що пішло, кому, з чим. - Mock - об'єкт з очікуваннями, заданими заздалегідь: «метод
chargeбуде викликано раз з сумою 100». Не збулося - тест падає. - Spy - записує всі виклики, а перевірки пишуть після дії. Як mock, але читається в порядку «дія → перевірка».
$this->mock(PaymentGateway::class, function (MockInterface $mock) {
$mock->expects('charge')->with(10000)->andReturn(new Charge('ch_1'));
});
Чому надмір моків шкодить: тест, що мокає п'ять залежностей і перевіряє порядок їхніх викликів, фіксує реалізацію, а не поведінку. Будь-який рефакторинг ламає тест, хоча для користувача нічого не змінилось, - і команда звикає «лагодити тести», а не довіряти їм.
Практичні правила:
- Мокати межі системи - зовнішні API, платежі, пошту, час - а не власні класи.
- Віддавати перевагу фейкам і перевірці стану: що записано в базу, що повернуто.
- Не мокати те, чим не володієте, напряму: краще обгорнути чужий SDK своїм інтерфейсом і підміняти його.
- Хоча б кілька тестів мають проганяти справжній ланцюжок без підмін - інакше зелений набір нічого не каже про продакшен.