Pytania rekrutacyjne Mockito

55 pytań — 20 odpowiedzi dostępnych od razu

Mockito jest częstym tematem rozmów o testach jednostkowych w ekosystemie Javy. Ten darmowy podgląd pytań rekrutacyjnych pozwala sprawdzić przykładowe odpowiedzi z puli 55 zagadnień.

Fiszki dotyczą tworzenia mocków, stubowania, matcherów argumentów oraz weryfikacji interakcji. Pomagają przygotować się do rozmowy o tym, kiedy izolować zależności i jak utrzymać testy czytelne.

Darmowy start

Pierwsze 20 odpowiedzi są dostępne od razu

Przeglądaj pełną listę 55 pytań. Pełne odpowiedzi, przykłady kodu i materiały do nauki odblokujesz w panelu.

Odblokuj 35 odpowiedzi

Podstawy Mockito

Czym jest Mockito i jakie problemy rozwiązuje w testowaniu kodu Java?

Odpowiedź w 30 sekund: Mockito to popularny framework do tworzenia obiektów typu mock w testach jednostkowych Javy. Pozwala odizolować testowaną klasę od jej zależności, dzięki czemu testy są szybkie, deterministyczne i skupione wyłącznie na logice jednej jednostki kodu.

Odpowiedź w 2 minuty: Mockito rozwiązuje problem testowania klas, które mają wiele zależności (bazy danych, usługi sieciowe, kolejki, inne serwisy). Zamiast uruchamiać prawdziwą bazę lub wołać prawdziwy serwis REST, podstawiamy mock — sztuczny obiekt, który zachowuje się tak, jak my mu każemy. To pozwala uniknąć efektów ubocznych i napisać test, który sprawdza wyłącznie zachowanie testowanej klasy.

Najczęstsze problemy, które Mockito rozwiązuje: izolacja testów (Single Responsibility w testach), kontrola nad zwracanymi wartościami zależności (when(...).thenReturn(...)), weryfikacja interakcji (verify(...)) — czyli sprawdzanie, czy testowany kod wywołał odpowiednią metodę z odpowiednimi argumentami. Dzięki temu możemy testować scenariusze trudne do odtworzenia w środowisku produkcyjnym — np. timeouty, błędy 500, puste odpowiedzi.

Mockito kładzie nacisk na czytelność API — używa stylu BDD (given/when/then), nie wymaga trybu record-replay (jak EasyMock) i działa z JUnit 4, JUnit 5 (@ExtendWith(MockitoExtension.class)) oraz TestNG.

Przykład kodu:

// Klasa testowana
class OrderService {
    private final PaymentGateway gateway;
    OrderService(PaymentGateway gateway) { this.gateway = gateway; }

    boolean place(Order order) {
        return gateway.charge(order.total()) == PaymentStatus.OK;
    }
}

// Test z Mockito
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
    @Mock PaymentGateway gateway;       // tworzy mock automatycznie
    @InjectMocks OrderService service;  // wstrzykuje mocki przez konstruktor

    @Test
    void powinienZlozycZamowienieGdyPlatnoscOK() {
        // given - definiujemy zachowanie zależności
        when(gateway.charge(100)).thenReturn(PaymentStatus.OK);

        // when - wywołujemy testowaną metodę
        boolean result = service.place(new Order(100));

        // then - sprawdzamy wynik i interakcję
        assertTrue(result);
        verify(gateway).charge(100);
    }
}

Materiały

Czym różni się mock, stub, fake i spy w terminologii xUnit Patterns?

Odpowiedź w 30 sekund: W terminologii Gerarda Meszarosa (xUnit Test Patterns) wszystkie te obiekty to "test doubles" — zamienniki produkcyjnych zależności. Różnią się przeznaczeniem: stub dostarcza gotowych odpowiedzi, mock dodatkowo weryfikuje interakcje, fake to działająca lekka implementacja, a spy to częściowy mock owijający prawdziwy obiekt.

Odpowiedź w 2 minuty: Stub zwraca z góry ustalone dane (state verification). Używamy go, gdy testowana metoda potrzebuje danych z zależności, ale nie obchodzi nas, czy i jak została wywołana. W Mockito stub to when(obj.method()).thenReturn(value).

Mock to stub plus weryfikacja interakcji (behavior verification). Sprawdzamy, czy testowany kod wywołał odpowiednią metodę zależności z odpowiednimi argumentami i odpowiednią liczbę razy. W Mockito użyjemy verify(mock).method(arg). W praktyce w Mockito każdy obiekt utworzony przez mock() może pełnić obie role.

Fake to lekka, ale działająca implementacja interfejsu — np. HashMap zamiast bazy danych, in-memory repository zamiast JpaRepository. Fake faktycznie wykonuje logikę, ale jest uproszczony — nadaje się do testów, ale nie do produkcji. Mockito sam fake'ów nie generuje — to ręcznie napisana klasa.

Spy to wrapper na prawdziwy obiekt. Domyślnie deleguje wywołania do oryginalnej implementacji, ale możemy nadpisać wybrane metody. Przydatny przy legacy code, gdzie nie da się łatwo wydzielić zależności. W Mockito: Mockito.spy(realObject). Uwaga — częste użycie spy często sygnalizuje, że kod wymaga refaktoryzacji.

Przykład kodu:

// Stub - tylko zwraca dane
List<String> stub = mock(List.class);
when(stub.get(0)).thenReturn("hello");
assertEquals("hello", stub.get(0));

// Mock - dodatkowo weryfikujemy wywołania
List<String> mock = mock(List.class);
mock.add("item");
verify(mock).add("item");      // czy metoda była wywołana?
verify(mock, times(1)).add(any());

// Fake - ręczna implementacja in-memory
class FakeUserRepo implements UserRepository {
    private final Map<Long, User> store = new HashMap<>();
    public User save(User u) { store.put(u.id(), u); return u; }
    public User findById(Long id) { return store.get(id); }
}

// Spy - opakowuje prawdziwy obiekt
List<String> spy = spy(new ArrayList<>());
spy.add("real");                // dodaje naprawdę
when(spy.size()).thenReturn(99); // ale size() jest podrasowane
assertEquals(99, spy.size());

Tabela porównawcza:

flowchart TD
    A["Test Doubles (xUnit Patterns)"] --> B[Stub]
    A --> C[Mock]
    A --> D[Fake]
    A --> E[Spy]
    B --> B1["Definicja: zwraca ustalone dane<br/>Zachowanie: brak weryfikacji<br/>Użycie: state verification"]
    C --> C1["Definicja: stub + weryfikacja<br/>Zachowanie: sprawdza interakcje<br/>Użycie: behavior verification"]
    D --> D1["Definicja: lekka implementacja<br/>Zachowanie: prawdziwa logika<br/>Użycie: złożone zależności in-memory"]
    E --> E1["Definicja: wrapper na real object<br/>Zachowanie: częściowe mockowanie<br/>Użycie: legacy code"]

Materiały

Jaka jest różnica między Mockito a innymi frameworkami mockującymi (EasyMock, JMockit, PowerMock)?

Odpowiedź w 30 sekund: Mockito jest dziś dominującym frameworkiem dzięki czytelnemu API w stylu given-when-then. EasyMock używa modelu record-replay (bardziej toporny), JMockit oferuje potężne mockowanie statyki i finali, a PowerMock to rozszerzenie do Mockito/EasyMock pozwalające mockować static, private i konstruktory — coraz rzadziej potrzebne, bo nowoczesne Mockito (od 3.x) potrafi to natywnie.

Odpowiedź w 2 minuty: Mockito to standardowy wybór w ekosystemie Java od ok. 2012 roku. Ma intuicyjne API (when().thenReturn(), verify()), dobre integracje z JUnit i Spring, i od wersji 3.4 potrafi mockować metody statyczne (mockStatic) oraz finalne klasy (przez mockito-inline).

EasyMock to starszy framework używający paradygmatu record-replay. Najpierw "nagrywamy" oczekiwane wywołania w trybie record, potem wywołujemy replay() i dopiero wtedy mock zachowuje się jak zaprogramowany. API jest mniej czytelne, dlatego EasyMock stracił popularność na rzecz Mockito.

JMockit to potężniejszy, ale bardziej skomplikowany framework używający agenta JVM (modyfikacja bytecode w runtime). Potrafi mockować praktycznie wszystko — static, final, private, konstruktory, a nawet java.lang.System. Cena to stroma krzywa uczenia i mniejsza społeczność.

PowerMock to nakładka na Mockito lub EasyMock dodająca możliwości mockowania static, private i konstruktorów (przez manipulację classloadera). Był popularny w czasach, gdy Mockito tego nie potrafiło. Dziś, gdy Mockito 3+ ma natywne mockStatic, PowerMock jest w zaniku — projekt nie jest już aktywnie rozwijany.

Przykład kodu:

// Mockito - given-when-then, intuicyjne
when(service.fetch()).thenReturn("data");
verify(service).fetch();

// EasyMock - record-replay (mniej czytelne)
expect(service.fetch()).andReturn("data");
replay(service);             // przełączenie w tryb replay
service.fetch();
verify(service);

// JMockit - expectations block
new Expectations() {{
    service.fetch(); result = "data";
}};

// PowerMock - mockowanie statyki (legacy podejście)
@RunWith(PowerMockRunner.class)
@PrepareForTest(Utils.class)
class Test {
    @Test void test() {
        mockStatic(Utils.class);
        when(Utils.now()).thenReturn(1000L);
    }
}

Porównanie frameworków:

flowchart LR
    M[Mockito] --> M1["Styl: given-when-then<br/>Static: tak (mockStatic od 3.4)<br/>Final: tak (mockito-inline)<br/>Popularność: ★★★★★<br/>Status: aktywny"]
    E[EasyMock] --> E1["Styl: record-replay<br/>Static: nie<br/>Final: nie natywnie<br/>Popularność: ★★<br/>Status: utrzymywany"]
    J[JMockit] --> J1["Styl: expectations block<br/>Static: tak (JVM agent)<br/>Final: tak<br/>Popularność: ★★<br/>Status: utrzymywany"]
    P[PowerMock] --> P1["Styl: rozszerza Mockito/EasyMock<br/>Static: tak (classloader hack)<br/>Final: tak<br/>Popularność: ★★ (zanik)<br/>Status: nieaktywny"]

Materiały

Jak dodać Mockito do projektu Maven i Gradle?

Odpowiedź w 30 sekund: W Mavenie dodajemy mockito-core (i opcjonalnie mockito-junit-jupiter dla integracji z JUnit 5) jako zależność w zakresie test. W Gradle używamy konfiguracji testImplementation. Dla mockowania klas finalnych i metod statycznych potrzebujemy dodatkowo mockito-inline.

Odpowiedź w 2 minuty: Mockito jest publikowane jako artefakty Maven Central pod groupId org.mockito. Najważniejsze artefakty to:

  • mockito-core — rdzeń biblioteki, wystarczy do większości testów.
  • mockito-junit-jupiter — integracja z JUnit 5 (@ExtendWith(MockitoExtension.class)).
  • mockito-inline — alternatywny mock maker pozwalający mockować klasy finalne, metody finalne i statyczne. Od Mockito 5 ten mock maker jest domyślny — wówczas mockito-core wystarcza.
  • mockito-bom — Bill of Materials, pozwala zarządzać wersjami wszystkich artefaktów spójnie.

W Spring Boot wystarczy zależność spring-boot-starter-test, która już zawiera Mockito w odpowiedniej wersji — wtedy nie deklarujemy jej osobno.

Pamiętaj o scope test w Mavenie lub testImplementation w Gradle — Mockito nie powinien trafiać do produkcyjnego classpath.

Przykład kodu:

<!-- Maven: pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>5.11.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-junit-jupiter</artifactId>
        <version>5.11.0</version>
        <scope>test</scope>
    </dependency>
    <!-- opcjonalnie dla Mockito < 5 -->
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-inline</artifactId>
        <version>5.2.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>
// Gradle: build.gradle
dependencies {
    testImplementation 'org.mockito:mockito-core:5.11.0'
    testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
    // opcjonalnie dla mockowania final/static przy Mockito < 5
    testImplementation 'org.mockito:mockito-inline:5.2.0'
}

// Gradle Kotlin DSL: build.gradle.kts
dependencies {
    testImplementation("org.mockito:mockito-core:5.11.0")
    testImplementation("org.mockito:mockito-junit-jupiter:5.11.0")
}

Materiały

Jakie są ograniczenia Mockito (final classes, static methods, private methods) i jak je obejść?

Odpowiedź w 30 sekund: Klasyczne Mockito (do 2.x) nie potrafiło mockować klas finalnych, metod statycznych ani prywatnych. Od Mockito 3.4 mamy mockStatic dla statyki, a od Mockito 5 domyślny mock maker inline pozwala mockować klasy/metody finalne. Metod prywatnych nadal nie da się mockować — należy je refaktorować lub testować przez metody publiczne.

Odpowiedź w 2 minuty: Klasy i metody finalne — historyczne ograniczenie wynikające z mechanizmu CGLib (Mockito tworzył podklasę mockowanej klasy, a finali nie można dziedziczyć). Rozwiązanie: artefakt mockito-inline (Mockito 2.7+) albo natywnie w Mockito 5 (gdzie inline jest domyślnym mock makerem). Inline używa Java Instrumentation API do modyfikacji bytecode.

Metody statyczne — od Mockito 3.4 dostępne jest Mockito.mockStatic(Class). Wymaga mockito-inline (lub Mockito 5). Należy używać try-with-resources, żeby mock działał tylko w scope testu i nie wpływał na inne testy.

Metody prywatne — Mockito nie wspiera ich i prawdopodobnie nie będzie. Filozofia jest taka, że metody prywatne to detal implementacji i powinny być testowane przez metody publiczne. Jeśli koniecznie musisz, opcje to: (1) refaktor — wyciągnięcie logiki do osobnej klasy/metody pakietowej, (2) refleksja (ReflectionTestUtils ze Spring Test), (3) PowerMock (ale to oznacza dług techniczny).

Konstruktory — od Mockito 3.5 jest mockConstruction(Class), działa podobnie do mockStatic.

Inne ograniczenia: Mockito nie mockuje equals(), hashCode(), toString() zachowuje domyślne. Nie mockuje też Optional ani enumów w sposób intuicyjny.

Przykład kodu:

// 1. Klasa finalna (Mockito 5 domyślnie, lub mockito-inline)
final class FinalService {
    String name() { return "real"; }
}

@Test
void mockFinalClass() {
    FinalService mock = mock(FinalService.class);
    when(mock.name()).thenReturn("mocked");
    assertEquals("mocked", mock.name());
}

// 2. Metoda statyczna - mockStatic
@Test
void mockStaticMethod() {
    try (MockedStatic<Math> mocked = mockStatic(Math.class)) {
        mocked.when(() -> Math.max(1, 2)).thenReturn(99);
        assertEquals(99, Math.max(1, 2));   // mock działa
    }
    assertEquals(2, Math.max(1, 2));        // tu już prawdziwa
}

// 3. Konstruktor - mockConstruction
@Test
void mockConstructor() {
    try (MockedConstruction<UserService> ignored =
             mockConstruction(UserService.class,
                 (mock, ctx) -> when(mock.find(1L)).thenReturn(new User("Anna")))) {
        UserService svc = new UserService();       // zwróci mock
        assertEquals("Anna", svc.find(1L).name());
    }
}

// 4. Metoda prywatna - przez refleksję (ostateczność)
class Holder { private String secret() { return "x"; } }
String result = (String) ReflectionTestUtils
    .invokeMethod(new Holder(), "secret");

Tabela ograniczeń:

flowchart TD
    O[Ograniczenia Mockito] --> F[Klasy finalne]
    O --> S[Metody statyczne]
    O --> P[Metody prywatne]
    O --> C[Konstruktory]
    F --> F1["Rozwiązanie: mockito-inline<br/>(Mockito 5 - domyślnie)<br/>Mechanizm: Java Instrumentation API"]
    S --> S1["Rozwiązanie: Mockito.mockStatic()<br/>Wymaga: mockito-inline / Mockito 5<br/>Zasada: try-with-resources"]
    P --> P1["Rozwiązanie: refleksja lub refactor<br/>Narzędzia: ReflectionTestUtils, PowerMock<br/>Zalecane: wyciągnij do klasy publicznej"]
    C --> C1["Rozwiązanie: Mockito.mockConstruction()<br/>Wymaga: Mockito 3.5+<br/>Zasada: try-with-resources"]

Materiały

Tworzenie mocków

6. Jak tworzyć mocki za pomocą Mockito.mock() i adnotacji @Mock?

Mockito udostępnia dwa równoważne sposoby tworzenia mocków: programatyczne wywołanie Mockito.mock(Class) oraz deklaratywną adnotację @Mock. Wybór zależy od stylu testów oraz tego, czy chcemy korzystać z integracji z JUnit.

Sposób 1: Mockito.mock() (programatyczny)

import static org.mockito.Mockito.*;

class UserServiceTest {

    @Test
    void shouldReturnUserById() {
        // Tworzenie mocka programatycznie
        UserRepository repository = mock(UserRepository.class);

        // Stubowanie zachowania
        when(repository.findById(1L)).thenReturn(new User("Anna"));

        UserService service = new UserService(repository);
        User user = service.getUser(1L);

        assertEquals("Anna", user.getName());
        verify(repository).findById(1L);
    }
}

Sposób 2: Adnotacja @Mock (deklaratywna)

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository repository;

    @InjectMocks
    private UserService service;

    @Test
    void shouldReturnUserById() {
        when(repository.findById(1L)).thenReturn(new User("Anna"));

        User user = service.getUser(1L);

        assertEquals("Anna", user.getName());
    }
}

Najważniejsze cechy:

  • Mockito.mock(Class) — natychmiastowe, działa wszędzie, nie wymaga frameworka testowego ani inicjalizacji.
  • @Mock — czytelniejsze przy wielu mockach, automatycznie nadaje sensowną nazwę mocka (dla komunikatów weryfikacji), wymaga aktywacji przez MockitoExtension, MockitoAnnotations.openMocks(this) lub MockitoJUnitRunner.
  • Oba sposoby tworzą ten sam typ mocka — różnią się tylko sposobem inicjalizacji.

Źródło: site.mockito.org

7. Czym różni się Mockito.mock(Class) od @Mock z MockitoAnnotations.openMocks lub MockitoExtension?

Choć rezultat jest funkcjonalnie taki sam, sposób uzyskania mocka oraz koszt utrzymania kodu testowego znacząco się różnią.

flowchart LR
    A["Mockito.mock(Class)<br/>(programatyczne)"] --> A1["Tworzy mock natychmiast<br/>w dowolnym miejscu"]
    A --> A2["Brak wymaganej<br/>inicjalizacji"]
    A --> A3["Nazwa mocka = nazwa klasy<br/>(mniej czytelne błędy)"]

    B["@Mock<br/>(deklaratywne)"] --> B1["Wymaga otwarcia:<br/>MockitoExtension<br/>lub openMocks(this)"]
    B --> B2["Nazwa mocka = nazwa pola<br/>(czytelne błędy)"]
    B --> B3["Współpracuje z @InjectMocks,<br/>@Spy, @Captor"]

Aktywacja @Mock — dwa warianty:

// Wariant 1: MockitoExtension (zalecany dla JUnit 5)
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock private PaymentGateway paymentGateway;
    @Mock private InventoryService inventory;

    @InjectMocks private OrderService orderService;
    // ...
}

// Wariant 2: MockitoAnnotations.openMocks() w try-with-resources
class OrderServiceTest {

    @Mock private PaymentGateway paymentGateway;
    @InjectMocks private OrderService orderService;

    private AutoCloseable mocks;

    @BeforeEach
    void setUp() {
        mocks = MockitoAnnotations.openMocks(this);
    }

    @AfterEach
    void tearDown() throws Exception {
        mocks.close(); // zwalnia zasoby (m.in. mocki inline static)
    }
}

Tabela porównawcza:

Cecha Mockito.mock(Class) @Mock
Styl Programatyczny Deklaratywny
Wymaga inicjalizacji? Nie Tak (MockitoExtension / openMocks)
Nazwa mocka w komunikatach Nazwa klasy (userRepository) Nazwa pola (repository)
Czytelność przy 5+ mockach Niska (powtarzalny kod) Wysoka
Integracja z @InjectMocks Manualna Automatyczna
Sprawdzanie nieużytych stubów Tylko ręcznie Domyślnie w MockitoExtension (STRICT)
Zwolnienie zasobów Automatyczne (GC) Wymaga close() przy openMocks

Praktyczna rada: w JUnit 5 używaj MockitoExtension — daje strict stubbing (wykrywa nieużyte when(...)), automatyczną inicjalizację i czysty kod. openMocks z try-with-resources jest potrzebny tylko, gdy nie możesz dodać rozszerzenia.

Źródło: site.mockito.org

8. Jak działa @Spy i kiedy używać go zamiast @Mock?

@Spy tworzy częściowy mock — opakowuje realny obiekt, zachowując domyślnie wszystkie jego prawdziwe zachowania. Możesz selektywnie nadpisać wybrane metody, podczas gdy @Mock zwraca wartości domyślne (null, 0, pusta kolekcja) dla wszystkich metod.

flowchart LR
    M["@Mock"] --> M1["Cała klasa zmockowana"]
    M --> M2["Metody zwracają wartości domyślne<br/>(null, 0, [], false)"]
    M --> M3["Zachowanie musisz<br/>zdefiniować przez when(...)"]

    S["@Spy"] --> S1["Realny obiekt opakowany"]
    S --> S2["Domyślnie używa prawdziwego<br/>kodu klasy"]
    S --> S3["Selektywnie nadpisujesz<br/>wybrane metody"]

Przykład:

@ExtendWith(MockitoExtension.class)
class CartTest {

    @Spy
    private ShippingCalculator shipping = new ShippingCalculator();

    @Mock
    private DiscountService discountService;

    @InjectMocks
    private Cart cart;

    @Test
    void shouldUseRealShippingButMockedDiscount() {
        // Realna metoda obliczy koszt
        BigDecimal cost = shipping.calculate(5);

        // Selektywnie nadpisz tylko jedną metodę
        doReturn(BigDecimal.ZERO).when(shipping).calculate(0);

        when(discountService.getDiscount(any())).thenReturn(0.1);

        cart.checkout();

        // Weryfikacja działa identycznie jak dla @Mock
        verify(shipping).calculate(anyInt());
    }
}

Tabela porównawcza:

Aspekt @Mock @Spy
Tworzony obiekt Pełny mock Wrapper na realny obiekt
Zachowanie domyślne metod Wartości domyślne (null, 0, ...) Prawdziwa implementacja klasy
Wymaga realnej instancji? Nie Tak (lub konstruktora bezargumentowego)
Składnia stubowania when(mock.foo()).thenReturn(x) doReturn(x).when(spy).foo() (preferowane)
Typowe użycie Zależności (repozytoria, usługi) Klasy legacy, częściowe zastępowanie
Ryzyko Brak — wszystko jest fałszywe Wywołanie prawdziwego kodu z side-effectami

Kiedy używać @Spy:

  • Testujesz klasę legacy, której nie da się łatwo rozdzielić na komponenty.
  • Chcesz przetestować realne zachowanie obiektu, ale podmienić jedną-dwie metody (np. zwracającą czas systemowy).
  • Pracujesz z klasą, w której większość metod ma sens w teście, ale jedna woła zewnętrzny system.

Pułapka: przy @Spy używaj doReturn(...).when(spy).method() zamiast when(spy.method()).thenReturn(...) — ta druga forma najpierw rzeczywiście wywołuje metodę szpiega, co przy side-effectach lub wyjątkach może zepsuć test.

Źródło: site.mockito.org

9. Czym jest @InjectMocks i jakie strategie wstrzykiwania obsługuje (constructor/setter/field)?

@InjectMocks instruuje Mockito, aby utworzyło instancję testowanej klasy i wstrzyknęło do niej mocki oznaczone @Mock lub @Spy. Mockito próbuje trzech strategii w określonej kolejności — pierwsza, która zadziała, jest używana.

flowchart TD
    Start([Mockito napotyka @InjectMocks]) --> C{1. Constructor injection<br/>czy istnieje konstruktor,<br/>do którego pasują mocki?}
    C -->|Tak| C1[Wybierz konstruktor o największej<br/>liczbie pasujących parametrów<br/>i wywołaj go z mockami]
    C -->|Nie| S{2. Setter injection<br/>czy obiekt ma settery<br/>dla pól typu mocków?}
    S -->|Tak| S1[Utwórz instancję bezargumentowym<br/>konstruktorem i wywołaj settery]
    S -->|Nie| F{3. Field injection<br/>czy są pola<br/>tego samego typu?}
    F -->|Tak| F1[Utwórz instancję bezargumentowym<br/>konstruktorem i ustaw pola<br/>przez refleksję]
    F -->|Nie| NULL[Pole pozostaje NULL<br/>NPE w teście]

    C1 --> End([Mocki wstrzyknięte])
    S1 --> End
    F1 --> End

Przykład — wszystkie trzy strategie:

// Klasa testowana ma konstruktor → constructor injection
public class OrderService {
    private final PaymentGateway gateway;
    private final InventoryService inventory;

    public OrderService(PaymentGateway gateway, InventoryService inventory) {
        this.gateway = gateway;
        this.inventory = inventory;
    }
}

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
    @Mock private PaymentGateway gateway;
    @Mock private InventoryService inventory;

    @InjectMocks private OrderService orderService; // → constructor injection
}
// Klasa testowana ma settery → setter injection
public class ReportGenerator {
    private DataSource dataSource;
    public ReportGenerator() {}
    public void setDataSource(DataSource ds) { this.dataSource = ds; }
}

@InjectMocks private ReportGenerator generator; // → setter injection
// Klasa testowana ma tylko pola → field injection (przez refleksję)
public class Notifier {
    @Inject private EmailClient emailClient;
    @Inject private SmsClient smsClient;
}

@InjectMocks private Notifier notifier; // → field injection

Najważniejsze pułapki:

  1. NULL field gdy żadna strategia nie pasuje — najczęstszy problem. Jeśli klasa ma konstruktor z parametrami, do których Mockito nie potrafi dopasować mocków (np. brakuje mocka, typy nie pasują), i równocześnie nie ma konstruktora bezargumentowego ani setterów, pole pozostaje null i pierwszy verify(...) rzuca NullPointerException.
  2. Wyjątek z konstruktora — gdy Mockito wybierze constructor injection, ale konstruktor rzuci wyjątek (np. walidacja parametrów), test wybuchnie na etapie inicjalizacji.
  3. Final fields tylko przez constructor — pola final nie da się ustawić ani przez setter, ani przez field injection.
  4. Niejednoznaczne dopasowanie — gdy w klasie są dwa pola tego samego typu, Mockito dopasowuje po nazwie pola (musi odpowiadać nazwie mocka).
  5. @InjectMocks NIE wywoła @PostConstruct ani logiki kontenera DI — to czysta refleksja Mockito.

Praktyczna rada: preferuj constructor injection — czyni zależności jawnymi, pozwala używać final, i jeśli czegoś brakuje, kod się nie skompiluje (zamiast zawieść w runtime).

Źródło: site.mockito.org

10. Jak mockować obiekty z parametrami generycznymi (np. List<String>)?

Z powodu type erasure w Javie typ generyczny List<String> w czasie wykonania jest zwykłym List. Mockito nie zna parametru generycznego, dlatego tworzenie mocka generycznego wymaga albo adnotacji @Mock, albo manualnego rzutowania.

flowchart LR
    A["Tworzenie mocka List&lt;String&gt;"] --> B{Sposób}

    B --> P["Mockito.mock(List.class)<br/>(programatyczny)"]
    P --> P1["Zwraca surowy List"]
    P --> P2["Wymaga rzutowania<br/>(List&lt;String&gt;) mock(List.class)"]
    P --> P3["Kompilator: WARNING<br/>unchecked cast"]
    P --> P4["@SuppressWarnings(unchecked)"]

    B --> D["@Mock private List&lt;String&gt; names<br/>(deklaratywny)"]
    D --> D1["Mockito używa typu pola"]
    D --> D2["Brak rzutowania w kodzie"]
    D --> D3["Brak warningu"]
    D --> D4["Czystsze, zalecane"]

Sposób 1: @Mock (zalecany — bez warningu)

@ExtendWith(MockitoExtension.class)
class TagServiceTest {

    @Mock
    private List<String> names;

    @Mock
    private Map<Long, User> userCache;

    @Test
    void shouldReturnTagsForUser() {
        when(names.get(0)).thenReturn("Anna");
        when(names.size()).thenReturn(3);

        assertEquals("Anna", names.get(0));
        assertEquals(3, names.size());
    }
}

Sposób 2: Mockito.mock() z rzutowaniem (warning unchecked)

class TagServiceTest {

    @Test
    @SuppressWarnings("unchecked") // konieczne — kompilator nie zna typu generycznego
    void shouldMockGenericList() {
        // Wymaga jawnego rzutowania
        List<String> names = (List<String>) mock(List.class);

        when(names.get(0)).thenReturn("Anna");
        assertEquals("Anna", names.get(0));
    }
}

Sposób 3: Wskazówka typu przez mock() z withSettings (nadal wymaga rzutowania)

@SuppressWarnings("unchecked")
List<String> names = mock(List.class, withSettings().name("namesList"));

Argumenty generyczne w stubowaniu — wskazówki typowe:

@Mock private Repository<User> userRepository;

@Test
void shouldHandleGenerics() {
    // Bez wskazówki typu — kompilator może mieć problem z inferencją
    when(userRepository.findAll()).thenReturn(List.of(new User("Anna")));

    // Dla matcherów argumentów używaj ArgumentMatchers.<T>any()
    when(userRepository.save(ArgumentMatchers.<User>any()))
            .thenAnswer(inv -> inv.getArgument(0));

    // Dla ArgumentCaptor — kapturuj w surowym typie lub forCaptor()
    @SuppressWarnings("unchecked")
    ArgumentCaptor<List<String>> captor = ArgumentCaptor.forClass(List.class);
}

Najważniejsze fakty:

  • Type erasure powoduje, że mock(List.class) zwraca surowy typ — kompilator zgłasza unchecked warning przy rzutowaniu.
  • @Mock jest czystszy — Mockito odczytuje typ pola przez refleksję i nie wymaga adnotacji @SuppressWarnings.
  • Dla ArgumentCaptor z typem generycznym (np. ArgumentCaptor<List<String>>) używaj ArgumentCaptor.forClass(List.class) z @SuppressWarnings("unchecked") lub od Mockito 3.4+ ArgumentCaptor.captor() (Java 8+, wnioskuje typ z miejsca wywołania).
  • W matcherach typu generycznego stosuj ArgumentMatchers.<T>any(), jeśli kompilator nie potrafi wywnioskować typu.

Źródło: site.mockito.org

Stubbing - definiowanie zachowania mocków

11. Jak stubować metody za pomocą when().thenReturn() i doReturn().when()?

Stubowanie to definiowanie zachowania mocka — co ma zwrócić, gdy zostanie wywołana konkretna metoda z konkretnymi argumentami. Mockito oferuje dwie podstawowe składnie: BDD-style when().thenReturn() oraz doReturn().when().

Składnia when().thenReturn() — najbardziej czytelna, używana w 90% przypadków:

import static org.mockito.Mockito.*;

@Test
void whenThenReturn_basicUsage() {
    UserRepository repo = mock(UserRepository.class);

    // stubowanie: gdy findById(1L) → zwróć Optional.of(user)
    when(repo.findById(1L)).thenReturn(Optional.of(new User("Anna")));
    when(repo.findById(2L)).thenReturn(Optional.empty());

    assertEquals("Anna", repo.findById(1L).get().getName());
    assertTrue(repo.findById(2L).isEmpty());
    // niezestubowane wywołanie zwraca default (null/empty/0/false)
    assertTrue(repo.findById(99L).isEmpty());
}

Składnia doReturn().when() — alternatywna kolejność wywołań, niezbędna w specyficznych przypadkach (void, spy, final):

@Test
void doReturnWhen_basicUsage() {
    UserRepository repo = mock(UserRepository.class);

    // składnia odwrócona: NAJPIERW doReturn, potem when(mock).method()
    doReturn(Optional.of(new User("Bob"))).when(repo).findById(1L);

    assertEquals("Bob", repo.findById(1L).get().getName());
}

Kluczowa różnica techniczna: when(repo.findById(1L)) faktycznie wywołuje metodę findById(1L) na mocku, żeby Mockito mogło zarejestrować, którą metodę stubujesz. Dla zwykłego mocka jest to bezpieczne (mock zwraca default), ale dla spy lub metod void to wywołanie byłoby problematyczne. doReturn(...).when(repo) najpierw zwraca specjalny stub, a dopiero potem rejestruje wywołanie — nie wykonuje realnej logiki.

Matchery argumentów — kiedy nie chcesz dopasować dokładnej wartości:

when(repo.findById(anyLong())).thenReturn(Optional.of(defaultUser));
when(service.process(eq("ADMIN"), anyInt())).thenReturn("OK");
// UWAGA: gdy używasz JEDNEGO matchera, WSZYSTKIE argumenty muszą być matcherami

Łańcuchowanie — kolejne zachowania można łączyć:

when(repo.count())
    .thenReturn(0L)        // pierwsze wywołanie
    .thenReturn(1L)        // drugie
    .thenThrow(new RuntimeException("boom"));  // trzecie i kolejne

Typowe scenariusze użycia:

  • when().thenReturn() — domyślny wybór dla mocków, czytelny w testach BDD,
  • doReturn().when() — dla metod void, spy, metod final, gdy realne wywołanie miałoby skutki uboczne,
  • thenAnswer() — gdy zwracana wartość zależy od argumentów (lambda otrzymuje InvocationOnMock),
  • thenCallRealMethod() — wywołaj prawdziwą implementację (rzadko, głównie przy częściowych mockach).

Źródła: site.mockito.org, javadoc.io/Mockito.

12. Kiedy używać when() a kiedy doReturn() - jakie są pułapki z metodami void i spy?

Wybór między when().thenReturn() a doReturn().when() nie jest tylko kwestią stylu — w niektórych sytuacjach when() po prostu nie zadziała lub spowoduje wykonanie prawdziwej logiki, czego absolutnie nie chcesz w teście.

Porównanie obu podejść:

flowchart TB
    Start[Stubowanie metody]
    Start --> Q1{Czy to spy<br/>partial mock?}
    Q1 -->|Tak| DoReturn1[Użyj doReturn().when()<br/>wywołanie when wykona REAL method]
    Q1 -->|Nie| Q2{Czy metoda<br/>jest void?}
    Q2 -->|Tak| DoReturn2[Użyj doThrow/doNothing/doAnswer<br/>when() nie skompiluje się dla void]
    Q2 -->|Nie| Q3{Czy potrzebujesz<br/>sekwencji wyjątków<br/>na metodach void?}
    Q3 -->|Tak| DoReturn3[Użyj doThrow().doNothing().when()]
    Q3 -->|Nie| WhenThen[Użyj when().thenReturn()<br/>czytelny BDD-style]

    style DoReturn1 fill:#ffcccc
    style DoReturn2 fill:#ffcccc
    style DoReturn3 fill:#ffe0cc
    style WhenThen fill:#ccffcc

Pułapka #1 — when() na spy wywołuje prawdziwą metodę. To najczęściej spotykany problem:

List<String> realList = new ArrayList<>();
List<String> spyList = spy(realList);

// PUŁAPKA: spyList.get(0) JEST wykonywane przed when() i rzuca IndexOutOfBoundsException!
when(spyList.get(0)).thenReturn("stubbed");  // ← wybuch

// POPRAWNIE: doReturn nie wywołuje get(0) na prawdziwym obiekcie
doReturn("stubbed").when(spyList).get(0);
assertEquals("stubbed", spyList.get(0));

Dla zwykłych mocków when(mock.get(0)) jest bezpieczne, bo mock zwraca null (default). Ale spy opakowuje realny obiekt — i get(0) na pustej liście naprawdę rzuca wyjątek, zanim Mockito zdąży zarejestrować stub.

Pułapka #2 — metody void nie kompilują się z when(). Konstrukcja when(mock.voidMethod()) nie skompiluje się, bo void nie jest wyrażeniem. Trzeba użyć:

EmailService service = mock(EmailService.class);

// metoda void send(String) — nie da się zrobić when(service.send("..."))
doNothing().when(service).send("welcome@example.com");  // domyślne dla mocka, ale jawnie
doThrow(new MailException()).when(service).send("invalid");

service.send("welcome@example.com");          // OK
assertThrows(MailException.class,
    () -> service.send("invalid"));

Pułapka #3 — sekwencje zachowań na metodach void. Łańcuchowanie do* działa tak samo jak thenReturn:

doNothing()
    .doThrow(new RuntimeException("second call fails"))
    .doNothing()
    .when(service).send(anyString());

service.send("a");  // OK (doNothing)
assertThrows(RuntimeException.class, () -> service.send("b"));  // wyjątek
service.send("c");  // OK znów

Pułapka #4 — metody final i static. W Mockito ≥ 3.4 (z mockito-inline) można mockować final i static, ale przy spy partial-mock metod final zalecane jest doReturn().

Reguła praktyczna:

  • Mock + metoda zwracająca wartość → when().thenReturn() (czytelność BDD),
  • Mock + metoda void → doNothing()/doThrow()/doAnswer(),
  • Spy (cokolwiek) → zawsze doReturn()/doThrow(),
  • Metoda zwraca skomplikowany typ generyczny → doReturn() (czasem omija problemy z type inference).

Źródła: site.mockito.org — Mockito.spy(), javadoc.io — doReturn.

13. Jak stubować metody rzucające wyjątki (thenThrow, doThrow)?

Testowanie ścieżek błędu wymaga symulowania wyjątków. Mockito udostępnia thenThrow() (dla metod zwracających wartość) oraz doThrow() (dla metod void i spy).

thenThrow() — metody zwracające wartość:

import static org.mockito.Mockito.*;

@Test
void thenThrow_basicUsage() {
    UserRepository repo = mock(UserRepository.class);

    // rzuć wyjątek po podaniu konkretnego argumentu
    when(repo.findById(999L))
        .thenThrow(new EntityNotFoundException("user 999"));

    // rzuć dla dowolnego argumentu
    when(repo.findByEmail(anyString()))
        .thenThrow(new RuntimeException("DB down"));

    assertThrows(EntityNotFoundException.class,
        () -> repo.findById(999L));
}

Możesz podać klasę wyjątku zamiast instancji — Mockito utworzy go z konstruktora bezargumentowego:

when(repo.findById(999L)).thenThrow(EntityNotFoundException.class);

doThrow() — metody void:

@Test
void doThrow_voidMethod() {
    EmailService service = mock(EmailService.class);

    // dla void NIE MOŻNA użyć when() — używamy doThrow().when().method()
    doThrow(new MailException("SMTP down"))
        .when(service).send("user@example.com");

    assertThrows(MailException.class,
        () -> service.send("user@example.com"));

    // inne argumenty nie rzucają — wciąż domyślne doNothing
    service.send("other@example.com");  // OK
}

Łączenie zachowań — sukces, błąd, znów sukces:

// metoda zwracająca wartość
when(repo.count())
    .thenReturn(0L)
    .thenThrow(new SQLException("timeout"))
    .thenReturn(42L);

// metoda void
doNothing()
    .doThrow(new RuntimeException("retry"))
    .doNothing()
    .when(service).send(anyString());

Wyjątki sprawdzane (checked exceptions) — Mockito sprawdza, czy deklarowana sygnatura metody pozwala na wyrzucenie danego typu. Jeśli void send(String) deklaruje throws MailException, możesz wyrzucić MailException. Próba wyrzucenia checked exception, którego metoda nie deklaruje, spowoduje błąd Mockito:

// jeśli metoda nie deklaruje throws IOException — błąd!
doThrow(new IOException()).when(service).send("x");
// rozwiązanie: użyj RuntimeException lub jego podklasy
doThrow(new UncheckedIOException("io fail", new IOException()))
    .when(service).send("x");

Typowe zastosowania:

  • Symulowanie awarii zewnętrznych zależności (DB, HTTP, message broker),
  • Testowanie obsługi błędów w warstwie serwisowej (try/catch, retry, fallback),
  • Wymuszanie ścieżki "exception path" w testach jednostkowych,
  • Symulowanie nieprzewidywalnych wyjątków od stron trzecich (OptimisticLockException, DataAccessException).

Spy + wyjątek — zawsze przez doThrow():

List<String> spyList = spy(new ArrayList<>());
doThrow(new IllegalStateException()).when(spyList).clear();

assertThrows(IllegalStateException.class, spyList::clear);

Źródła: site.mockito.org — thenThrow, javadoc.io — doThrow.

14. Jak działają kolejne wywołania (thenReturn(a, b, c)) i kiedy je stosować?

Mockito pozwala zdefiniować różne wartości zwracane dla kolejnych wywołań tej samej metody. Jest to przydatne, gdy testujesz logikę z retry, paginacją, polling, lub stanem zmieniającym się w czasie.

Składnia varargs — thenReturn(a, b, c):

import static org.mockito.Mockito.*;

@Test
void consecutiveCalls_varargs() {
    PollingService poller = mock(PollingService.class);

    when(poller.status())
        .thenReturn("PENDING", "PENDING", "READY");

    assertEquals("PENDING", poller.status());  // 1. wywołanie
    assertEquals("PENDING", poller.status());  // 2. wywołanie
    assertEquals("READY",   poller.status());  // 3. wywołanie
    assertEquals("READY",   poller.status());  // 4. i kolejne — STICKY LAST
}

Kluczowa zasada — "sticky last value": po wyczerpaniu listy Mockito powtarza ostatnią wartość w nieskończoność. To zachowanie jest dokumentowane i zamierzone — chroni przed IndexOutOfBoundsException w testach z większą liczbą wywołań niż się spodziewano.

flowchart LR
    C1[1. wywołanie] -->|zwraca| A[wartość a]
    C2[2. wywołanie] -->|zwraca| B[wartość b]
    C3[3. wywołanie] -->|zwraca| Cv[wartość c]
    C4[4. wywołanie] -->|zwraca| Cv
    C5[5. wywołanie] -->|zwraca| Cv
    Cn[N-te wywołanie<br/>n ≥ 3] -->|zawsze zwraca| Cv

    style Cv fill:#cce5ff,stroke:#0066cc,stroke-width:2px
    style C4 fill:#fff4cc
    style C5 fill:#fff4cc
    style Cn fill:#fff4cc

Składnia łańcuchowa — równoważna:

when(poller.status())
    .thenReturn("PENDING")
    .thenReturn("PENDING")
    .thenReturn("READY");

Mieszanie zachowań — wartość + wyjątek:

when(api.fetch())
    .thenReturn(response1)
    .thenReturn(response2)
    .thenThrow(new TimeoutException("3rd call failed"))
    .thenReturn(response3);  // 4. wywołanie znów OK

api.fetch();  // response1
api.fetch();  // response2
assertThrows(TimeoutException.class, () -> api.fetch());  // 3.
api.fetch();  // response3 (sticky)

Generyki i varargs — pułapka kompilatora:

// PROBLEM: varargs z Optional<User> może źle się zdedukować
when(repo.findById(1L)).thenReturn(Optional.empty(), Optional.of(user));
// czasem kompilator zwraca błąd "incompatible types"

// ROZWIĄZANIE: explicit cast lub łańcuch
when(repo.findById(1L))
    .thenReturn(Optional.empty())
    .thenReturn(Optional.of(user));

Kiedy stosować kolejne wywołania:

  • Retry / circuit breaker — pierwsze N wywołań rzuca wyjątek, kolejne zwracają sukces:
    when(api.call())
        .thenThrow(new TimeoutException())
        .thenThrow(new TimeoutException())
        .thenReturn(successResponse);
    
  • Polling / status checking — symulacja stanu zmieniającego się w czasie (PENDING → READY),
  • Paginacja — kolejne strony wyników:
    when(repo.findPage(any())).thenReturn(page1, page2, emptyPage);
    
  • Cache miss → hit — pierwsze wywołanie zwraca null/empty, kolejne zwracają wartość,
  • Token refresh — pierwszy token wygasa, drugi działa.

Alternatywa — thenAnswer() dla bardziej złożonych scenariuszy:

AtomicInteger counter = new AtomicInteger();
when(api.fetch()).thenAnswer(inv -> {
    int n = counter.incrementAndGet();
    return n < 3 ? null : "value";
});

Źródła: site.mockito.org, javadoc.io — Stubbing consecutive calls.

15. Jak stubować metody asynchroniczne i metody zwracające CompletableFuture?

Coraz więcej API w Javie zwraca CompletableFuture<T> (REST clients, reactive integrations, async repositories). Mockito nie wymaga żadnego specjalnego API — wystarczy zwrócić odpowiednią instancję CompletableFuture.

Zwracanie ukończonego future — sukces:

import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;

@Test
void completedFuture_success() throws Exception {
    AsyncUserService service = mock(AsyncUserService.class);

    when(service.findByIdAsync(1L))
        .thenReturn(CompletableFuture.completedFuture(new User("Anna")));

    User user = service.findByIdAsync(1L).get();  // .get() blokuje do zakończenia
    assertEquals("Anna", user.getName());
}

Zwracanie future zakończonego wyjątkiem:

@Test
void failedFuture() {
    AsyncUserService service = mock(AsyncUserService.class);

    when(service.findByIdAsync(999L))
        .thenReturn(CompletableFuture.failedFuture(
            new EntityNotFoundException("user 999")));

    assertThrows(ExecutionException.class,
        () -> service.findByIdAsync(999L).get());
    // ExecutionException opakowuje oryginalny EntityNotFoundException
}

Dla Javy < 9, gdzie nie ma CompletableFuture.failedFuture():

CompletableFuture<User> failed = new CompletableFuture<>();
failed.completeExceptionally(new EntityNotFoundException("user 999"));
when(service.findByIdAsync(999L)).thenReturn(failed);

Dynamiczne stubowanie przez thenAnswer() — gdy odpowiedź zależy od argumentów:

when(service.findByIdAsync(anyLong())).thenAnswer(invocation -> {
    Long id = invocation.getArgument(0);
    if (id < 0) {
        return CompletableFuture.failedFuture(
            new IllegalArgumentException("negative id"));
    }
    return CompletableFuture.completedFuture(new User("user-" + id));
});

assertEquals("user-5", service.findByIdAsync(5L).get().getName());
assertThrows(ExecutionException.class,
    () -> service.findByIdAsync(-1L).get());

Symulacja opóźnienia (delay) — przydatne dla testów timeoutów:

when(service.findByIdAsync(1L)).thenAnswer(inv ->
    CompletableFuture.supplyAsync(() -> {
        try { Thread.sleep(500); } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return new User("slow");
    })
);

CompletableFuture<User> future = service.findByIdAsync(1L);
// teraz możesz testować timeout, orTimeout, anyOf itp.
assertThrows(TimeoutException.class,
    () -> future.get(100, TimeUnit.MILLISECONDS));

Stubowanie reaktywnych typów (Mono/Flux z Project Reactor):

when(reactiveRepo.findById(1L))
    .thenReturn(Mono.just(new User("Anna")));

when(reactiveRepo.findAll())
    .thenReturn(Flux.just(user1, user2, user3));

when(reactiveRepo.findById(999L))
    .thenReturn(Mono.error(new EntityNotFoundException()));

Stubowanie Future<T> (klasyczny java.util.concurrent.Future):

ExecutorService executor = mock(ExecutorService.class);
Future<String> fakeFuture = CompletableFuture.completedFuture("done");
when(executor.submit(any(Callable.class))).thenReturn(fakeFuture);

Praktyczne zasady:

  • Najczęstszy przypadek → thenReturn(CompletableFuture.completedFuture(value)) — synchroniczna odpowiedź, future natychmiast gotowy, idealny dla testów logiki biznesowej.
  • Test obsługi błędów → CompletableFuture.failedFuture(exception) lub completeExceptionally().
  • Test concurrency / timeouts → supplyAsync(...) z Thread.sleep() lub własnym Executor.
  • Test pełnego pipeline'u async → rozważ użycie CompletableFuture.runAsync(...) z kontrolowanym executorem (np. single-thread) — łatwiej zweryfikować kolejność.
  • Unikaj Thread.sleep() w testach — preferuj Awaitility (await().until(...)) do oczekiwania na zakończenie future.

Verify dla metod async:

service.findByIdAsync(1L);
service.findByIdAsync(2L);

verify(service).findByIdAsync(1L);
verify(service).findByIdAsync(2L);
verify(service, times(2)).findByIdAsync(anyLong());

Źródła: site.mockito.org, javadoc.io — Mockito.thenAnswer, docs.oracle.com — CompletableFuture.

Argument matchers

16. Czym są argument matchers w Mockito (any(), eq(), argThat())?

Argument matchers to specjalne metody Mockito, które pozwalają dopasować argumenty wywołania metody według wzorca, a nie według konkretnej wartości. Używamy ich, gdy nie znamy lub nie chcemy specyfikować dokładnej wartości argumentu przekazywanego do mockowanej metody. Matchers wykorzystują wewnętrznie mechanizm Hamcrest i działają w trybie ArgumentMatchers (statycznym imporcie).

Najpopularniejsze matchery:

import static org.mockito.ArgumentMatchers.*;

// any() - pasuje do dowolnego argumentu (również null)
when(userService.findById(any())).thenReturn(user);

// any(Class) - pasuje do dowolnego argumentu danego typu
when(userService.save(any(User.class))).thenReturn(savedUser);

// anyString(), anyInt(), anyLong() - typowane matchery (nie null!)
when(repo.findByName(anyString())).thenReturn(user);
when(repo.findById(anyLong())).thenReturn(user);

// eq() - pasuje do konkretnej wartości (używany gdy MIESZAMY z innymi matcherami)
when(service.transfer(eq(100), anyString())).thenReturn(true);

// argThat() - własna logika dopasowania (lambda lub klasa)
when(repo.save(argThat(u -> u.getAge() > 18))).thenReturn(savedUser);

// isNull(), notNull(), isA(Class) - pomocnicze matchery
when(service.process(isNull())).thenThrow(new IllegalArgumentException());
graph LR
    A[Argument Matchers] --> B[any]
    A --> C[anyString]
    A --> D[anyInt/Long]
    A --> E[eq value]
    A --> F[argThat custom]
    A --> G[isNull/notNull]

    B -->|dowolny obiekt + null| H[Object/Generic]
    C -->|dowolny String, nie null| I[String]
    D -->|dowolna liczba| J[primitive]
    E -->|konkretna wartość| K[literal]
    F -->|własna logika lambda| L[Predicate]
    G -->|null check| M[null/not-null]

Matchery upraszczają stubbing i weryfikację, gdy testujemy zachowanie, a nie dokładną wartość argumentu - np. weryfikujemy, że metoda została wywołana z jakimkolwiek użytkownikiem, bez konieczności konstruowania identycznego obiektu.

Źródło: site.mockito.org - ArgumentMatchers

17. Dlaczego nie można mieszać surowych wartości z matcherami w jednym wywołaniu?

To kluczowa zasada Mockito: jeśli używasz choćby jednego argument matchera w wywołaniu metody, WSZYSTKIE pozostałe argumenty muszą również być matcherami. Mieszanie surowych wartości (literali) z matcherami powoduje błąd InvalidUseOfMatchersException.

Dlaczego tak się dzieje technicznie?

Matchery Mockito nie zwracają wartości - one rejestrują się na wewnętrznym stosie (ArgumentMatcherStorage). Gdy wywołujesz when(mock.method(anyInt(), "literal")), Mockito widzi:

  • pozycja 0: matcher anyInt ze stosu
  • pozycja 1: literal "literal" (brak matchera na stosie!)

Mockito nie wie, czy "literal" to argument, czy może brakuje matchera - i wyrzuca wyjątek.

Błędne użycie (wyrzuca wyjątek):

// ŹLE - miks literal + matcher
when(service.transfer(100, anyString())).thenReturn(true);
// InvalidUseOfMatchersException: 2 matchers expected, 1 recorded

Poprawne użycie - opakowanie literali w eq():

// DOBRZE - wszystko jako matchery
when(service.transfer(eq(100), anyString())).thenReturn(true);

// Werifikacja również wymaga konsekwencji
verify(service).transfer(eq(100), eq("USD"));
// LUB bez matcherów w ogóle:
verify(service).transfer(100, "USD");

Typowe rozwiązania:

// Same literale - bez matcherów
when(repo.findById(42L)).thenReturn(user);

// Wszystkie matchery
when(repo.findByNameAndAge(anyString(), anyInt())).thenReturn(user);

// Mix - literale opakowane w eq()
when(service.save(eq("Jan"), any(Address.class))).thenReturn(true);

Źródło: site.mockito.org - Argument matchers

18. Jak tworzyć własne argument matchery za pomocą ArgumentMatcher lub ArgumentMatchers.argThat?

Gdy wbudowane matchery nie wystarczają, możemy tworzyć własne matchery dwoma sposobami: za pomocą lambdy w argThat() (krótkie, jednorazowe sprawdzenia) lub implementując interfejs ArgumentMatcher<T> (wielokrotne użycie, czytelne komunikaty błędów).

Sposób 1: argThat() z lambdą (Java 8+)

Idealne do prostych, jednorazowych sprawdzeń bezpośrednio w teście:

import static org.mockito.ArgumentMatchers.argThat;

// Lambda - sprawdza długość stringa
when(repo.findByName(argThat(s -> s.length() > 5)))
    .thenReturn(user);

// Lambda - sprawdza pole obiektu
when(userService.save(argThat(u -> u.getAge() >= 18 && u.getEmail() != null)))
    .thenReturn(savedUser);

// Werifikacja z argThat
verify(emailService).send(argThat(msg ->
    msg.getSubject().contains("Welcome") &&
    msg.getTo().endsWith("@example.com")
));

Sposób 2: Implementacja ArgumentMatcher<T> - reużywalna klasa

Lepsze dla skomplikowanej logiki i wielokrotnego użycia. Dodatkowo metoda toString() poprawia czytelność komunikatów błędów:

import org.mockito.ArgumentMatcher;

class IsAdultUser implements ArgumentMatcher<User> {
    @Override
    public boolean matches(User user) {
        return user != null && user.getAge() >= 18;
    }

    @Override
    public String toString() {
        // Komunikat w razie błędu weryfikacji
        return "<adult user (age >= 18)>";
    }
}

// Użycie w teście
when(repo.save(argThat(new IsAdultUser()))).thenReturn(savedUser);
verify(notificationService).notify(argThat(new IsAdultUser()));

Sposób 3: Statyczna metoda fabrykująca - najbardziej czytelnie

public class UserMatchers {
    public static User adultUser() {
        return argThat(u -> u != null && u.getAge() >= 18);
    }

    public static User userWithEmail(String email) {
        return argThat(u -> email.equals(u.getEmail()));
    }
}

// W teście - czytelnie jak DSL
when(repo.save(adultUser())).thenReturn(savedUser);
verify(emailService).send(userWithEmail("jan@example.com"));

Kiedy używać którego:

Sposób Kiedy stosować
argThat(lambda) Proste, jednorazowe sprawdzenia w jednym teście
ArgumentMatcher<T> klasa Powtarzalne wzorce, lepsze komunikaty błędów
Statyczne fabryki Wspólne matchery dla wielu testów, DSL-style

Źródło: site.mockito.org - ArgumentMatcher

19. Jak działa ArgumentCaptor i kiedy go używać zamiast matcherów?

ArgumentCaptor to mechanizm Mockito do przechwytywania argumentów, z którymi wywołano mockowaną metodę, a następnie do wykonania na nich asercji (np. JUnit/AssertJ). W przeciwieństwie do matcherów, captor przechowuje rzeczywistą wartość i pozwala ją inspekcjonować po wywołaniu.

Filozofia użycia:

  • Matcher - używamy podczas stubbingu (when().thenReturn()) i prostej weryfikacji
  • Captor - używamy podczas weryfikacji gdy chcemy dokładnie sprawdzić złożony argument
sequenceDiagram
    participant Test
    participant Mock
    participant Captor as ArgumentCaptor

    Test->>Captor: ArgumentCaptor.forClass(User.class)
    Test->>Mock: serviceUnderTest.register(jan)
    Note over Mock: mock.save(jan) jest wywołane wewnętrznie
    Test->>Mock: verify(mock).save(captor.capture())
    Mock-->>Captor: zapisuje argument 'jan'
    Test->>Captor: captor.getValue()
    Captor-->>Test: zwraca przechwycony obiekt User
    Test->>Test: assertEquals/assertThat na polach

Podstawowe użycie - tworzenie przez forClass():

import org.mockito.ArgumentCaptor;

@Test
void shouldSaveUserWithCorrectData() {
    // given
    UserService service = new UserService(userRepoMock);
    ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);

    // when
    service.register("Jan", "jan@example.com", 25);

    // then - przechwyć argument
    verify(userRepoMock).save(captor.capture());

    // Asercje na przechwyconej wartości
    User captured = captor.getValue();
    assertEquals("Jan", captured.getName());
    assertEquals("jan@example.com", captured.getEmail());
    assertEquals(25, captured.getAge());
    assertTrue(captured.getCreatedAt().isBefore(Instant.now()));
}

Użycie z adnotacją @Captor:

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock private UserRepository repo;
    @Captor private ArgumentCaptor<User> userCaptor;
    @Captor private ArgumentCaptor<List<String>> tagsCaptor;  // działa z generykami!

    @Test
    void shouldCaptureMultipleInvocations() {
        UserService service = new UserService(repo);
        service.registerAll(List.of("Jan", "Anna", "Piotr"));

        // capture() zbiera WSZYSTKIE wywołania
        verify(repo, times(3)).save(userCaptor.capture());

        List<User> allUsers = userCaptor.getAllValues();
        assertEquals(3, allUsers.size());
        assertEquals("Anna", allUsers.get(1).getName());
    }
}

Captor vs Matcher - praktyczne porównanie:

// MATCHER - dobre dla prostych asercji "czy w ogóle wywołano"
verify(emailService).send(argThat(msg -> msg.getSubject().contains("Welcome")));

// CAPTOR - lepsze dla wielu asercji na tym samym obiekcie
ArgumentCaptor<Email> emailCaptor = ArgumentCaptor.forClass(Email.class);
verify(emailService).send(emailCaptor.capture());
Email sent = emailCaptor.getValue();
assertThat(sent.getSubject()).contains("Welcome");
assertThat(sent.getTo()).isEqualTo("jan@example.com");
assertThat(sent.getBody()).isNotEmpty();
assertThat(sent.getAttachments()).hasSize(2);

Kiedy używać Captora:

  • Sprawdzasz wiele pól złożonego obiektu
  • Chcesz porównać obiekt z czymś wygenerowanym dynamicznie (np. timestamp)
  • Chcesz inspekcjonować wszystkie wywołania w pętli (getAllValues())
  • Chcesz lepsze komunikaty błędów niż przy argThat() (AssertJ je dostarcza)

Źródło: site.mockito.org - ArgumentCaptor

20. Jakie są typowe pułapki użycia any(Class) dla typów generycznych (warnings, NullPointerException)?

Używanie any() i any(Class) wymaga ostrożności - są trzy typowe pułapki: problem z typami generycznymi (warnings), NullPointerException na primitive types, oraz nieoczekiwane dopasowanie null.

Pułapka 1: NullPointerException przy primitive types

Metoda any() zwraca null. Gdy Mockito przekazuje null do metody, która oczekuje primitive (int, long, boolean), Java próbuje rozpakować null przez Integer.intValue() - co rzuca NullPointerException.

public interface Calculator {
    int add(int a, int b);  // primitive int!
}

// ŹLE - any() zwraca null, primitive int nie może być null
when(calc.add(any(), any())).thenReturn(5);
// NullPointerException przy unboxing

// DOBRZE - użyj dedykowanych matcherów dla primitive
when(calc.add(anyInt(), anyInt())).thenReturn(5);
when(repo.findById(anyLong())).thenReturn(user);
when(flag.check(anyBoolean())).thenReturn(true);

Pułapka 2: Generyki i warning unchecked

W typach generycznych any(Class) powoduje warning z powodu type erasure:

// Warning: unchecked conversion
when(service.process(any(List.class))).thenReturn(result);

// Lepiej - bezpieczny cast lub po prostu any()
when(service.process(ArgumentMatchers.<List<String>>any())).thenReturn(result);

// Najczęściej wystarczy any() - kompilator wywnioskuje typ z kontekstu
when(service.process(any())).thenReturn(result);  // bez warning

Pułapka 3: any() dopasowuje również null

Od Mockito 2.0 any() dopasowuje null oraz dowolny obiekt - to różnica względem anyString(), anyList(), które NIE dopasowują null:

// any() - łapie też null!
when(repo.findByName(any())).thenReturn(user);
repo.findByName(null);  // zwraca user

// anyString() - NIE łapie null
when(repo.findByName(anyString())).thenReturn(user);
repo.findByName(null);  // NIE pasuje - zwraca default (null)

// Jeśli chcesz wyraźnie "non-null string"
verify(repo).findByName(anyString());  // weryfikuje wywołanie z not-null

Tabela porównawcza matcherów any*():

Matcher Dopasowuje NIE dopasowuje Kiedy używać
any() dowolny obiekt + null nic Najbardziej ogólnie, generyki
any(MyClass.class) obiekty MyClass + null inne typy Gdy ważny jest typ (np. polimorfizm)
anyString() dowolny String (not null) null, inne typy Stringi gdy null jest osobnym przypadkiem
anyInt(), anyLong() dowolna liczba primitive null OBOWIĄZKOWO dla primitive int/long
anyBoolean() true lub false null OBOWIĄZKOWO dla primitive boolean
anyList(), anyMap() kolekcje (not null) null Kolekcje gdy null jest osobnym przypadkiem
isNull() tylko null wszystko inne Test ścieżki obsługi null

Praktyczne wskazówki:

// 1. Primitive - ZAWSZE używaj typowanych matcherów
when(service.calculate(anyInt(), anyDouble())).thenReturn(42.0);

// 2. Wrapper types (Integer, Long) - any() jest OK
when(service.calculate(any(Integer.class), any(Double.class))).thenReturn(42.0);

// 3. Generyki - any() wystarczy, typ z kontekstu
when(repo.<User>findByPredicate(any())).thenReturn(user);

// 4. Jeśli chcesz wymusić not-null
when(repo.save(notNull())).thenReturn(saved);

Źródło: site.mockito.org - ArgumentMatchers.any()

Weryfikacja interakcji

21. 21. Jak weryfikować że metoda została wywołana (verify(), times(), never())?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
22. 22. Czym różnią się atLeast, atMost, atLeastOnce, atMostOnce?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
23. 23. Jak weryfikować kolejność wywołań za pomocą InOrder?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
24. 24. Czym jest verifyNoInteractions i verifyNoMoreInteractions?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
25. 25. Jak weryfikować argumenty wywołania metody na mocku (verify + matchers + captor)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Spy - częściowe mocki

26. 26. Czym jest @Spy i jak różni się od @Mock (real method calls vs mock behavior)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
27. 27. Kiedy warto używać @Spy zamiast @Mock (legacy code, refactoring)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
28. 28. Jakie są pułapki @Spy (real method calls, side effects, NullPointerException)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
29. 29. Jak stubować metody na spy bez wywoływania prawdziwej implementacji (doReturn().when())?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Konfiguracja i resetowanie

30. 30. Jak resetować mocki za pomocą Mockito.reset() i kiedy tego unikać?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
31. 31. Czym jest MockSettings i jak konfigurować mocki (withSettings())?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
32. 32. Jak ustawić domyślną odpowiedź mocków (Answers.RETURNS_SMART_NULLS, RETURNS_DEEP_STUBS)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
33. 33. Jak działa MockingDetails i kiedy się przydaje (debugowanie testów)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Static i final methods (Mockito 3+)

34. 34. Jak mockować metody statyczne w Mockito 3.4+ (mockStatic)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
35. 35. Jak działa mockConstruction do mockowania new instances?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
36. 36. Dlaczego mockowanie static/final było wcześniej niemożliwe (proxy, bytecode manipulation)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
37. 37. Kiedy warto mockować static/final, a kiedy to znak złej architektury?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Integracja z JUnit i Spring

38. 38. Jak integrować Mockito z JUnit 5 (@ExtendWith(MockitoExtension.class))?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
39. 39. Czym różni się MockitoAnnotations.openMocks(this) od MockitoExtension?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
40. 40. Jak działa @MockBean w Spring Boot Test i jak różni się od @Mock?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
41. 41. Czym jest @SpyBean i kiedy go używać zamiast @MockBean?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
42. 42. Jak testować kontrolery Spring MVC z mockowanymi serwisami?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Wzorce i najlepsze praktyki

43. 43. Jakie są dobre praktyki dotyczące nazewnictwa mocków i organizacji testów?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
44. 44. Czym jest BDDMockito (given/when/then) i jakie ma zalety?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
45. 45. Jak unikać over-mockingu i testowania szczegółów implementacji?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
46. 46. Kiedy warto przejść na real objects zamiast mocków (Classicist vs Mockist TDD)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
47. 47. Jak testować klasy z wieloma zależnościami bez nadmiernego mockowania?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Pułapki i debugowanie

48. 48. Jakie są typowe błędy Mockito (UnfinishedStubbingException, MissingMethodInvocationException)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
49. 49. Dlaczego stubowanie metody na finalnym polu klasy nie działa?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
50. 50. Jak debugować testy gdy mock nie zwraca oczekiwanej wartości?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
51. 51. Czym jest StrictStubbing i jakie wymusza zasady?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Migracja i kompatybilność

52. 52. Jakie są kluczowe zmiany między Mockito 3.x a 5.x (Java 11+, bytecode manipulation)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
53. 53. Jak migrować testy z PowerMock do Mockito 3+/5+?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
54. 54. Jak rozwiązać problem z mockowaniem klas Kotlin (final by default)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
55. 55. Jakie są strategie testowania kodu legacy z Mockito (refactoring → seam → mock)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Chcesz poznać wszystkie odpowiedzi?

Uzyskaj pełny dostęp do 55 pytań rekrutacyjnych z Mockito oraz pozostałych technologii.

Zobacz plany cenowe