I would like to inject a Mockito mock object into a Spring (3+) bean for the purposes of unit testing with JUnit. My bean dependencies are currently injected by using the @Autowired annotation on private member fields.

I have considered using ReflectionTestUtils.setField but the bean instance that I wish to inject is actually a proxy and hence does not declare the private member fields of the target class. I do not wish to create a public setter to the dependency as I will then be modifying my interface purely for the purposes of testing.

I have followed some advice given by the Spring community but the mock does not get created and the auto-wiring fails:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>

The error I currently encounter is as follows:

...
Caused by: org...NoSuchBeanDefinitionException:
    No matching bean of type [com.package.Dao] found for dependency:
    expected at least 1 bean which qualifies as autowire candidate for this dependency.
    Dependency annotations: {
        @org...Autowired(required=true),
        @org...Qualifier(value=dao)
    }
at org...DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(D...y.java:901)
at org...DefaultListableBeanFactory.doResolveDependency(D...y.java:770)

If I set the constructor-arg value to something invalid no error occurs when starting the application context.


Solution 1:

The best way is:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock"> 
    <constructor-arg value="com.package.Dao" /> 
</bean> 

Update
In the context file this mock must be listed before any autowired field depending on it is declared.

Solution 2:

@InjectMocks
private MyTestObject testObject;

@Mock
private MyDependentObject mockedObject;

@Before
public void setup() {
        MockitoAnnotations.initMocks(this);
}

This will inject any mocked objects into the test class. In this case it will inject mockedObject into the testObject. This was mentioned above but here is the code.

Solution 3:

I have a very simple solution using Spring Java Config and Mockito:

@Configuration
public class TestConfig {

    @Mock BeanA beanA;
    @Mock BeanB beanB;

    public TestConfig() {
        MockitoAnnotations.initMocks(this); //This is a key
    }

    //You basically generate getters and add @Bean annotation everywhere
    @Bean
    public BeanA getBeanA() {
        return beanA;
    }

    @Bean
    public BeanB getBeanB() {
        return beanB;
    }
}

Solution 4:

Given:

@Service
public class MyService {
    @Autowired
    private MyDAO myDAO;

    // etc
}

You can have the class that is being tested loaded via autowiring, mock the dependency with Mockito, and then use Spring's ReflectionTestUtils to inject the mock into the class being tested.

@ContextConfiguration(classes = { MvcConfiguration.class })
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
    @Autowired
    private MyService myService;

    private MyDAO myDAOMock;

    @Before
    public void before() {
        myDAOMock = Mockito.mock(MyDAO.class);
        ReflectionTestUtils.setField(myService, "myDAO", myDAOMock);
    }

    // etc
}

Please note that before Spring 4.3.1, this method won't work with services behind a proxy (annotated with @Transactional, or Cacheable, for example). This has been fixed by SPR-14050.

For earlier versions, a solution is to unwrap the proxy, as described there: Transactional annotation avoids services being mocked (which is what ReflectionTestUtils.setField does by default now)

Solution 5:

If you're using Spring Boot 1.4, it has an awesome way of doing this. Just use new brand @SpringBootTest on your class and @MockBean on the field and Spring Boot will create a mock of this type and it will inject it into the context (instead of injecting the original one):

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {

    @MockBean
    private RemoteService remoteService;

    @Autowired
    private Reverser reverser;

    @Test
    public void exampleTest() {
        // RemoteService has been injected into the reverser bean
        given(this.remoteService.someCall()).willReturn("mock");
        String reverse = reverser.reverseSomeCall();
        assertThat(reverse).isEqualTo("kcom");
    }

}

On the other hand, if you're not using Spring Boot or are you using a previous version, you'll have to do a bit more work:

Create a @Configuration bean that injects your mocks into Spring context:

@Configuration
@Profile("useMocks")
public class MockConfigurer {

    @Bean
    @Primary
    public MyBean myBeanSpy() {
        return mock(MyBean.class);
    }
}

Using @Primary annotation you're telling spring that this bean has priority if no qualifier are specified.

Make sure you annotate the class with @Profile("useMocks") in order to control which classes will use the mock and which ones will use the real bean.

Finally, in your test, activate userMocks profile:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
@ActiveProfiles(profiles={"useMocks"})
public class YourIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the mock!


    @Test
    public void test() {
        ....
    }
}

If you don't want to use the mock but the real bean, just don't activate useMocks profile:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
public class AnotherIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the real implementation!


    @Test
    public void test() {
        ....
    }
}