Thursday, July 16, 2009

Spring integration tests with mocked collaborators

Sometimes when you write integration tests that load Spring application context, you want to mock some collaborators just to verify that they've been called with correct data. This is a quite legitimate approach, but you have to keep in mind that Spring caches application contexts during the test execution. That means, after you replace a bean with a mock, all subsequent tests that share the same application context will use mocked instance instead of real one. That might cause tests to fail in very peculiar way. For example, test succeeds in Eclipse but fails in command line; you create new test and the old one starts failing, etc.

To fix the problem, you need to reload application context after every test that uses mocks. The best way to do this is to use @DirtiesContext annotation. In Spring 2.5 this was a method level annotation, but starting with Spring 3.0RC1 you can use it on the class level (thanks Spring!). So the rule of thumb is:

If you mock a bean in the Spring integration test, annotate the test class with @DirtiesContext

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:appclicationContext.xml"})
@DirtiesContext
public class IntegrationTest {

@Autowired
private Application application;

private Collaborator mockCollaborator;

@Before
public void setUp() {
mockCollaborator = mock(Collaborator.class);
application.setCollaborator(mockCollaborator);

}

@Test
public void collaborator_is_called_once() {
...
verify(mockCollaborator, times(1)).methodCall(...);
}
}

Resources

Source files for this post
• Spring annotations documentation
Class level @DirtiesContext annotation
• How to build Spring 3

No comments: