Tarin Gamberini

A software engineer and a passionate java programmer

Testing with the appropriate injection

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, etc., until when the same test that had failed before failed again! The cause was the same as 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 occur 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 Ottinger Agile in Flash

I guess the problem with this test was not a 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 losing isolation and gaining integration.

Even though 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.