Dlaczego w ogóle używamy Mocków i Stubów?
Podczas testowania jednostkowego naszym celem jest sprawdzenie działania jednej konkretnej klasy lub funkcji w całkowitej izolacji od reszty systemu. Nie chcemy w tym momencie testować bazy danych, połączeń HTTP, plików na dysku ani logiki innych komponentów. Zależy nam na tym, żeby test był szybki, przewidywalny i niezależny.
Wyobraź sobie, że testujesz klasę OrderService, która odpowiada za tworzenie zamówień. Aby wykonać swoją pracę, korzysta z kilku zewnętrznych usług, takich jak PaymentGateway, NotificationService czy OrderRepository. Gdybyś chciał przetestować tę klasę „na żywo”, musiałbyś:
- połączyć się z prawdziwą bazą danych,
- zrealizować płatność przez API,
- wysłać realne powiadomienie e-mail lub SMS.
To nie tylko niepraktyczne — to wręcz niebezpieczne i kosztowne, szczególnie w środowisku testowym.
Dlatego używamy Mocków i Stubów — specjalnych atrap (czyli obiektów zastępczych), które:
- Stuby – zwracają z góry określone odpowiedzi na dane wywołania metod, nie wykonując prawdziwej logiki (np. zwracają gotowego użytkownika zamiast go pobierać z bazy),
- Mocki – dodatkowo pozwalają sprawdzić, czy dana metoda została wywołana, z jakimi parametrami i ile razy (np. czy powiadomienie zostało faktycznie „wysłane”).
Dzięki temu możemy dokładnie przetestować, jak OrderService zachowuje się w różnych sytuacjach — niezależnie od tego, czy działa realne API płatności czy nie.
To podejście przynosi ogromne korzyści:
- Testy są szybkie – nie czekają na żadne zewnętrzne usługi.
- Są niezawodne – nie psują się, jeśli zewnętrzny system jest chwilowo niedostępny.
- Są precyzyjne – skupiają się tylko na konkretnej logice, którą chcemy sprawdzić.
Mówiąc krótko: Mocki i Stuby to fundament pisania testowalnego kodu. Bez nich trudno byłoby utrzymać testy w dużych, złożonych systemach, gdzie komponenty są ze sobą silnie powiązane.
Stub — co to jest?
Stub to obiekt zastępczy, który podstawia z góry określoną wartość w miejscu, gdzie normalnie wywoływana byłaby metoda zależna od zewnętrznych danych — bazy danych, API lub innych klas.
Przykład:
Wyobraź sobie, że pracujesz w obsłudze klienta jednej z top firm w Polsce. Dzwonisz do klienta, aby zapytać o jego zadowolenie z usługi. Niestety — klient nie odbiera. W takim wypadku Twój kolega z biurka obok mówi:
“Wyobraźmy sobie, że klient powiedział, że jest zadowolony, żebyśmy mogli przetestować dalszy przebieg rozmowy.”
To właśnie stub – udaje, że coś się wydarzyło, żeby można było przećwiczyć zachowanie systemu.
Wyobraź sobie, że masz klasę WeatherService, która pobiera temperaturę z zewnętrznego API.
class WeatherService { public function getTemperature(): int { // Tu byłoba prawdziwa implementacja połączenia z API return 23; } }
Teraz testujemy klasę Thermostat, która korzysta z WeatherService.
class Thermostat { public function __construct(private WeatherService $service) {} public function isTooCold(): bool { return $this->service->getTemperature() < 18; } }
W teście nie chcemy czekać na API — tworzymy stub:
$stub = $this->createStub(WeatherService::class); $stub->method('getTemperature')->willReturn(15); $thermostat = new Thermostat($stub); $this->assertTrue($thermostat->isTooCold());
🔹 Stub po prostu zwraca wartość.
Mock — co to jest?
Mock to obiekt zastępczy, który nie tylko udaje działanie prawdziwej klasy, ale również pozwala sprawdzić, czy konkretne metody zostały wywołane, z jakimi parametrami i ile razy.
Przykład:
Załóżmy, że jesteś szefem kuchni i chcesz sprawdzić, czy kelner złożył zamówienie na danie “Zupa dnia”. Nie interesuje Cię, czy kelner dostał potwierdzenie — chcesz wiedzieć, czy wykonał konkretną czynność.
W testach to właśnie robi mock – pozwala zweryfikować, czy dane zdarzenie miało miejsce.
Poniżej masz klasę Notifier, która wysyła powiadomienie:
class Notifier { public function send(string $message): void { // Wysyłanie maila lub push notification } }
Klasa OrderService tworzy zamówienie i wysyła powiadomienie:
$mock = $this->createMock(Notifier::class); $mock->expects($this->once()) ->method('send') ->with("Nowe zamówienie utworzone"); $orderService = new OrderService($mock); $orderService->createOrder();
🔹 Mock nie tylko podstawia, ale też weryfikuje zachowanie.
Kiedy używać Stuba, a kiedy Mocka?
Użyj STUBA, gdy:
- Chcesz tylko zasymulować zwracaną wartość.
- Zależy Ci na uproszczeniu testu i nie interesuje Cię, co dzieje się wewnątrz.
Użyj MOCKA, gdy:
- Chcesz sprawdzić, czy metoda została wywołana.
- Testujesz komunikację między klasami.
- Budujesz testy oparte na zachowaniu (behavioral tests).
Sytuacja | Użyj | Dlaczego |
Chcesz tylko ustawić wartość zwracaną | Stub | Prosty, szybki |
Chcesz sprawdzić, czy metoda została wywołana | Mock | Możesz zweryfikować zachowanie |
Testujesz logikę warunkową na podstawie danych | Stub | Potrzebujesz tylko symulacji |
Testujesz interakcję między klasami | Mock | Liczy się, czy coś się wydarzyło |
Jakie narzędzia warto znać?
W PHP najczęściej używamy PHPUnit — to standard w testowaniu.
🔧 PHPUnit
📦 Instalacja przez Composer: composer require --dev phpunit/phpunit
📚 Dokumentacja: https://phpunit.de/manual/
Zawiera metody createStub() i createMock() — wystarczą na 90% przypadków.
🧪 Mockery (dla bardziej zaawansowanych przypadków)
Jeśli chcesz pisać mocki w bardziej “elastyczny” sposób (np. gdy obiekty są trudne do zamockowania), spróbuj:
composer require --dev mockery/mockery
Zamiast:
$mock = $this->createMock(Notifier::class);
Możesz napisać:
$mock = \Mockery::mock(Notifier::class); $mock->shouldReceive('send')->once()->with("Nowe zamówienie utworzone");
Podsumowanie
- Stub — do podstawiania wartości. Używaj, gdy chcesz “oszukać” zależność.
- Mock — do sprawdzania, czy dana metoda została użyta. Używaj, gdy testujesz interakcje.
- PHPUnit — wystarczy na start i więcej.
- Mockery — dla większej kontroli i wygody.
Jeśli dopiero zaczynasz — zacznij od prostych stubów i dodawaj mocki tam, gdzie to ma sens. Praktyka to najlepszy nauczyciel. Dzięki testom z użyciem mocków i stubów Twój kod stanie się bardziej pewny, modularny i łatwiejszy do utrzymania.
Nikt jeszcze nie komentował. Bądź pierwszy!