Last time we talked about the mocking injection issue with Spring in testing, and discussed the option of using Spring’s “prototype” injection. We figured out, this solution is not for everyone.
What can we do with a regular, Spring singleton mock?
Resetting mocks
Mockito has a nifty little method called reset. As you can guess, it resets everything about a mock (or list of mocks). It resets all expectations, and in our case, method call tracking.
So, we can rewrite our Spring integration tests (using the original singleton configuration) like this:
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = { MockPublisherConfiguration.class })
public class MockPublisherResetTests {
@Autowired Publisher mockPublisher;
@Before
public void setup() {
Mockito.reset(mockPublisher);
}
@Test
public void firstTest() {
mockPublisher.publish();
Mockito.verify(mockPublisher).publish();
}
@Test
public void secondTest() {
verify(mockPublisher, never()).publish();
}
}
Code language: JavaScript (javascript)
The mockPublisher is @Autowired again, a singleton like in the original integration test class, and is injected once. Before each integration test, we reset the mock to start from scratch (we can also use @After for the same effect), and presto! Both integration tests pass.
In order for this method to work, we need to add a @Before method, and don’t forget call reset. It’s a bit manual, and if people forget it, they tests quickly fail and remind them.
However…
There’s a minor (read: huge) problem with this method. Since there’s only a singleton instance in our Spring application, reset resets the mock object for everyone who’s got a reference of it. Not just for our tests, but also for other consumers.
Suppose our configuration looked like this:
@Configuration
public class MockPublisherConfiguration {
@Bean
public Publisher mockPublisher() {
Publisher mockPublisher = mock(Publisher.class);
doThrow(RuntimeException.class).when(mockPublisher)
.publishWithError();
return mockPublisher;
}
}
Code language: JavaScript (javascript)
The doThrow behavior setting, that everyone should have enjoyed, is reset also whenever Mockito.reset is called. So even if our tests don’t use it, it may fail other tests as well that planned to use it.
So here’s a tip for you. When creating mocks in configuration classes, don’t don’t do anything but create them. Additional behaviors should be set only in the tests.
Now that we got this off our chests, there’s one more option we’ll explore next time: the humble MockBean.
0 Comments