Why field injection causes class is difficult to test?

Viewed 1997

According Google I/O 2009 - Big Modular Java with Guice (at 29:55) using field injection makes testing harder than constructor or setter injection.

Let's consider NewsService class:

public class NewsService{

    @Inject Storage storage;
    @Inject Authenticator auth;

    public void addNews(String data){
       if(auth.authenticate()){
          storage.save(data);
       }
    }
}

and unit test for NewsService:

@RunWith(MockitoJunitRunner.class)
public class NewsServiceTest{

    @Mock Storage storage;
    @Mock Authenticator auth;
    @InjectMocks NewsService sut; //system under test

    @Test
    public void addNews_ShouldCallSaveOnStorage_WhenAuthIsSucceed(){
       when(auth.authenticate()).thenReturn(true);
       sut.addNews("mocked data");
       verify(storage).save("mocked data");
    }
}

Mockito can inject mocks even to private fields. Taking care about encapsulation is important, so I use package visibility. Even if Storage and Authenticator classes are in different package than NewsService and both still use package visibility for their own fields, I don't care about it, because NewsService don't have to access any field from Storage or Authenticator, because I mocked their behaoviour.

Even if I would change NewsService to use constructor injection, my unit test class wouldn't change.

So the question is: does field injection really causes testing harder with keeping well-designed architecture?

1 Answers

Field injection is more difficult because there's necessarily reflection involved; for example, if you were testing a class with constructor injection, you'd just create mock dependencies and pass them to new. Writing directly to private fields requires a good bit of bookkeeping.

Note, however, that that talk is more than four years old. As you demonstrate, tools for dependency injection (both for testing and production) have come a long way since then, and @Inject is the official Java standard now. You can use a test framework to handle all of the reflection for you and easily test objects with injected dependencies.

Related