Injecting Mockito mocks into a Spring bean

后端 未结 22 1176
庸人自扰
庸人自扰 2020-11-22 09:44

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

相关标签:
22条回答
  • 2020-11-22 10:00

    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.

    0 讨论(0)
  • 2020-11-22 10:00

    I found a similar answer as teabot to create a MockFactory that provides the mocks. I used the following example to create the mock factory (since the link to narkisr are dead): http://hg.randompage.org/java/src/407e78aa08a0/projects/bookmarking/backend/spring/src/test/java/org/randompage/bookmarking/backend/testUtils/MocksFactory.java

    <bean id="someFacade" class="nl.package.test.MockFactory">
        <property name="type" value="nl.package.someFacade"/>
    </bean>
    

    This also helps to prevent that Spring wants to resolve the injections from the mocked bean.

    0 讨论(0)
  • 2020-11-22 10:01

    Perhaps not the perfect solution, but I tend not to use spring to do DI for unit tests. the dependencies for a single bean (the class under test) usually aren't overly complex so I just do the injection directly in the test code.

    0 讨论(0)
  • 2020-11-22 10:04

    As of Spring 3.2, this is no longer an issue. Spring now supports Autowiring of the results of generic factory methods. See the section entitled "Generic Factory Methods" in this blog post: http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/.

    The key point is:

    In Spring 3.2, generic return types for factory methods are now properly inferred, and autowiring by type for mocks should work as expected. As a result, custom work-arounds such as a MockitoFactoryBean, EasyMockFactoryBean, or Springockito are likely no longer necessary.

    Which means this should work out of the box:

    <bean id="dao" class="org.mockito.Mockito" factory-method="mock">
        <constructor-arg value="com.package.Dao" />
    </bean>
    
    0 讨论(0)
  • 2020-11-22 10:09

    I would suggest to migrate your project to Spring Boot 1.4. After that you can use new annotation @MockBean to fake your com.package.Dao

    0 讨论(0)
  • 2020-11-22 10:10

    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() {
            ....
        }
    }
    
    0 讨论(0)
提交回复
热议问题