How to mock bean and avoiding NoUniqueBeanDefiniti

2019-07-19 00:01发布

问题:

I have webApplication based on SpringMVC and all beans are difined through annotations. Today I try to write test for my controllers. I put first one and tried to mock service which are used in this controllor. All this I did on the following way:

1) create context file for test clientControllerTest-context.xml

<import resource="classpath:spring-context.xml" />

<bean id="ConnectionDB" name="ConnectionDB" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="biz.podoliako.carwash.services.impl.ConnectDB" />
</bean>

where spring-context.xml is a main context file which are used in my webApplication (including information about real ConnectionDB bean)

2)create test class with following configuation

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:controllers/clientControllerTest-context.xml"})
@WebAppConfiguration

when the test was run, the NoUniqueBeanDefinitionException exeption was catched. I hoped that spring overwrite bean ConnectionDB but in fact it found 2 beans with one name and spring cannot chose which one has to be used.

Please, explaine me how can I use my main spring-context and mock one of the bean for testing and avoiding NoUniqueBeanDefinitionException if it possibe ofcause.

NB: I think createing context with all configuration for test is a bad idea therefore I try to use my main spring-context.

回答1:

You can define profiles for your Spring app, and tie your @Configuration classes or @Bean methods (or even your custom meta-annotations if you use any) to named profiles, even multiple profiles if you want. Then you could define profiles with names test, development, production or whatever custom scenario comes to your mind, and have your beans registered to your Spring context based on the currently active profile(s).

Define your beans for both the test and anotherTest profile like this:

@Configuration
@Profile({"test", "anotherTest"})
public class SomeConfig {...}

Or in an xml file:

...
<beans profile="test, anotherTest">
    <bean .../>
</beans>
...

Define your production restricted beans in a similar way to avoid conflict of beans in your profiles. Then, use the @ActiveProfiles annotation on your JUnit test class to indicate what profiles you want to be active while running the tests:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(...)
@ActiveProfiles({"test", "anotherTest"})

See this other question Default profile in Spring 3.1 on how to activate a profile by default in a webapp or the javadoc for org.springframework.core.env.Environment if you want to do it programatically.