Appearance
@Mock as a Constructor or Method Parameter
The previous chapter used @Mock and @InjectMocks on fields:
java
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
PaymentGateway gateway;
@Mock
OrderRepository repo;
@InjectMocks
OrderService service;
@Test
void placeOrder_success() {
when(gateway.charge(100)).thenReturn(true);
assertTrue(service.placeOrder(100));
verify(repo).save();
}
}MockitoExtension uses BeforeEachCallback to create the mocks and AfterEachCallback to clean up. But it implements a third interface:
java
public class MockitoExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolverWhy ParameterResolver
Recall from the extensions chapter: when JUnit calls a method or constructor with parameters, it asks every registered ParameterResolver whether it supports each parameter (supportsParameter) and, if so, to supply the value (resolveParameter).
MockitoExtension supports parameters annotated with @Mock and resolves each one by creating a mock of the parameter's type. So @Mock works on constructor and method parameters too, wherever JUnit resolves parameters.
Constructor parameters
java
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
private final PaymentGateway gateway;
private final OrderRepository repo;
private final OrderService service;
OrderServiceTest(@Mock PaymentGateway gateway, @Mock OrderRepository repo) {
this.gateway = gateway;
this.repo = repo;
this.service = new OrderService(gateway, repo);
}
@Test
void placeOrder_success() {
when(gateway.charge(100)).thenReturn(true);
boolean result = service.placeOrder(100);
assertTrue(result);
verify(repo).save();
}
}When JUnit creates the test instance, it resolves the two constructor parameters through MockitoExtension, which returns a mock for each.
Pros
- Clean, in the same constructor-injection style as production code.
- The fields can be
final.
Cons
Only safe with the default
PER_METHODlifecycle. The mocks are created when the test instance is created. WithPER_CLASSthere is one instance, so all tests share the same mocks, and stubs and call history leak:java@TestInstance(TestInstance.Lifecycle.PER_CLASS) @ExtendWith(MockitoExtension.class) class OrderServiceTest { final PaymentGateway gateway; OrderServiceTest(@Mock PaymentGateway gateway) { this.gateway = gateway; } @Test void a() { gateway.charge(1); verify(gateway).charge(1); } // passes @Test void b() { gateway.charge(1); verify(gateway).charge(1); } // FAILS: "But was 2 times" }@Mockfields do not have this problem:MockitoExtensionrecreates them before every test, even withPER_CLASS.You build the object under test yourself (
new OrderService(gateway, repo)), which is usually the clearest choice anyway.@InjectMockswould still work here, because Mockito also injects mocks it finds in the test's fields, but mixing the two styles makes a test harder to follow.
Test method parameters
java
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Test
void placeOrder_success(@Mock PaymentGateway gateway, @Mock OrderRepository repo) {
OrderService service = new OrderService(gateway, repo);
when(gateway.charge(100)).thenReturn(true);
boolean result = service.placeOrder(100);
assertTrue(result);
verify(repo).save();
}
}The mocks exist only for this test method. That is fully isolated, but every test has to declare its own mocks and build its own object under test: repetitive when a class has many tests. It suits a mock that only one or two tests need.
Which to use
| Style | Mocks live | Watch out for |
|---|---|---|
@Mock fields (+ @InjectMocks) | recreated before each test | @InjectMocks injection rules |
@Mock constructor parameters | as long as the test instance | PER_CLASS lifecycle shares them |
@Mock method parameters | one test method | repetition across tests |
All three are correct. With constructor parameters, keep the default per-method lifecycle.
Next: With the mocks in place, the last two chapters cover what you do with them. Chapter 18 covers every way to stub: return values, exceptions, void methods, argument matchers and dynamic answers.
Check yourself
- Which three JUnit extension interfaces does
MockitoExtensionimplement, and what is each for? - Why can
@Mockbe used on a test constructor's parameters? - Give two benefits of receiving mocks through the constructor.
- Why are constructor-parameter mocks risky with
@TestInstance(PER_CLASS), and why are@Mockfields not? - When are method-parameter mocks a good fit, and what is their drawback?
Answers
BeforeEachCallback(create mocks, handle@InjectMocks),AfterEachCallback(finish the session, release state, check stubs),ParameterResolver(supply@Mockparameters).- JUnit resolves constructor parameters through registered
ParameterResolvers, andMockitoExtensionsupports parameters annotated@Mock. - Clean constructor-injection style; the fields can be
final. - The constructor runs once per test instance; with
PER_CLASSthat is once for all tests, so the mocks and their history are shared.@Mockfields are recreated by the extension before every test. - When only one or a few tests need a mock; the drawback is repeating the mocks and setup in every test.