Tarin Gamberini

A software engineer and a passionate java programmer

Testing with the appropriate injection

Times ago I added some new functionalities to a Spring-based web application.

After some changes a unit test failed. The cause was a missing bean which stopped the auto-wiring process while Spring was loading the application context.

The test declared its own application contest, by using the annotation @Configuration on a static inner class; there were some mocked beans, so I added the missing bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MyClassTest {

    ...

    @Configuration
    static class ContextConfiguration {

        @Bean
        public MissingBean missingBean() {
            return new MockMissingBean();
        }

        @Bean
        public ThatBean thatBean() {
            return new MockThatBean();
        }

        @Bean
        public ThisBean thisBean() {
            return new MockThisBean();
        }

        ...
    }

}

Then I changed other classes adding other auto-wired beans, new methods, and the like, till when the same test that had failed before failed again! The cause was the same that had happened before: a missing bean. Clearly the missing bean was one of those I just added.

That brittle test was getting quite annoying. Why a unit test should ever fail when changes occurs in classes different from the tested one? Shouldn’t a unit test have to test a class in Isolation? Yes it should, more precisely the FIRST properties a unit test should have are:

Fast
Isolated
Repeatable
Self-verifying
Timely

Jeff Langr and Tim OttingerAgile in Flash

I guess the problem with this test was a not clear comprehension of injection techniques. In fact the test in its first implementation was isolated in practice, because it used mock objects injected by @RunWith(SpringJUnit4ClassRunner.class). Unfortunately this implementation was not isolated in theory too, because of the auto-wiring mechanism enabled by @RunWith(SpringJUnit4ClassRunner. If the class under test evolved it would be wired with every field annotated with @Autowired loosing isolation and gaining integration.

Even if in the Spring documentation there’s actually written unit and integration tests:

Spring TestContext Framework offers full integration with JUnit 4 through a custom runner (supported on JUnit 4.12 or higher). By annotating test classes with @RunWith(SpringJUnit4ClassRunner.class) or the shorter @RunWith(SpringRunner.class) variant, developers can implement standard JUnit 4 based unit and integration tests and simultaneously reap the benefits of the TestContext framework such as support for loading application contexts, dependency injection of test instances, transactional test method execution, and so on.

the «Spring JUnit 4 Runner» sub-section is under the «Integration Test» section, not under the «Unit Test» one.

In the end I isolated the test injecting mock objects, without any auto-wiring mechanism, by using the awesome Mockito framework for unit tests:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock
    public MissingBean missingBean;

    @Mock
    public ThatBean thatBean;

    @Mock
    public ThisBean thisBean;

    @InjectMocks
    private MyClass myClass;

    ...

}

Post a comment

A comment is submitted by an ordinary e-mail. Your e-mail address will not be published or broadcast.

This blog is moderated, therefore some comments might not be published. Comments are usually approved by the moderator in one/three days.