If we want to unit test a Spring managed bean with a private field that is annotated with for example the @Autowired
annotation, we must do something special. Normally we cannot access the private field to assign for example a stub implementation for testing, because we have to use the public setter method. But Spring allows the use of the @Autowired
annotation on a private field. And that means we don't have a public setter method. We must use org.springframework.test.util.ReflectionTestUtils.setField()
to assign a new value to the field. We pass the object which contains the private field, the field name and value to the method and our value is assigned to the private variable. So we can use this method to assign a stub implementation to the field and use it for testing.
// Class to test: src/main/java/com/mrhaki/spring/MyService.java package com.mrhaki.spring; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class MyService { @Autowired private MessageService messageService; public String say(String name) { return messageService.getMessage() + name; } }
// Support class used in MyService: src/main/java/com/mrhaki/spring/MessageService.java package com.mrhaki.spring; import org.springframework.stereotype.Component; @Component public class MessageService { public String getMessage() { return "Hello, "; } }
We want to unit test MyService
and provide a mock implementation for MessageService
, so we only test the code in MyService
. We use Mockito to provide the mock functionality. And because messageService
is a private field we must use ReflectionTestUtils.setField()
method.
// Test class for testing MyService: src/test/java/com/mrhaki/spring/MyServiceTest.java package com.mrhaki.spring; import org.junit.Test; import org.springframework.test.util.ReflectionTestUtils; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class MyServiceTest { @Test public void sayHi() { MessageService messageService = mock(MessageService.class); when(messageService.getMessage()).thenReturn("Hi, "); MyService myService = new MyService(); // Inject mock into private field: ReflectionTestUtils.setField(myService, "messageService", messageService); assertEquals("Hi, mrhaki", myService.say("mrhaki")); } }
As a bonus we can use the following Gradle build script to compile and test these classes:
apply plugin: 'java' repositories { mavenCentral() } dependencies { compile 'org.springframework:spring-context:3.0.4.RELEASE' testCompile 'junit:junit:4.8.1', 'org.mockito:mockito-all:1.8.5', 'org.springframework:spring-test:3.0.4.RELEASE' }
Project is also available on GitHub: BlogSamples/SpringTestInjection.