Overriding an Autowired Bean in Unit Tests
SpringSpring BootSpring AnnotationsSpring Problem Overview
Is there a simple way I can easily override an autowired bean in specific unit tests? There is only a single bean of every type in the compile classes so it's not a problem for autowiring in this case. The test classes would contain additional mocks. When running a unit test I'd simply like to specify an additional Configuration that says basically, while running this unit test use this mock instead of the standard bean.
Profiles seem a bit overkill for what I require and I'm not sure this would be achievable with the Primary annotation as different unit test could have different mocks.
Spring Solutions
Solution 1 - Spring
If you just simply want to provide a different bean in your tests, i think you don't need to use spring profiles or mockito.
Just do the following:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { TestConfig.class })
public class MyTest
{
@Configuration
@Import(Application.class) // the actual configuration
public static class TestConfig
{
@Bean
public IMyService myService()
{
return new MockedMyService();
}
}
@Test
public void test()
{
....
}
}
NOTE: tested with spring boot 1.3.2 / spring 4.2.4
Solution 2 - Spring
In Spring Boot 1.4 there's a simple way for doing that:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { MyApplication.class })
public class MyTests {
@MockBean
private MyBeanClass myTestBean;
@Before
public void setup() {
...
when(myTestBean.doSomething()).thenReturn(someResult);
}
@Test
public void test() {
// MyBeanClass bean is replaced with myTestBean in the ApplicationContext here
}
}
Solution 3 - Spring
I had similar problem and I solved with a mix and I find this one more useful and reusable. I created a spring profile for the tests and a config class that overrides the beans I want to mock in a very simple way:
@Profile("test")
@Configuration
@Import(ApplicationConfiguration.class)
public class ConfigurationTests {
@MockBean
private Producer kafkaProducer;
@MockBean
private SlackNotifier slackNotifier;
}
By doing that I can @Autowire those mock beans and use mockito to verify on them. Main advantage is that now all tests seamlessly get the mock beans without any per-test change. Tested with:
> spring boot 1.4.2
Solution 4 - Spring
You should use spring profiles in order to know what kind of bean you want to use in different contexts.
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html
Solution 5 - Spring
Since Spring Boot 1.4.0 instead of explicitly specifying @Configuration
for tests, simply add static nested class annotated with @TestConfiguration
and provide your replacement @Bean
annotated with @Primary
.
@TestConfiguration
will be added to your primary Spring Boot test context (which means your production bean will still be created), but the one from @TestConfiguration
will be used, because of the @Primary
.
Solution 6 - Spring
As mats.nowak commented, @ContextConfiguration
is useful for this.
Say a parent test class is like:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/some-dao-stuff.xml"
,"classpath:spring/some-rest-stuff.xml"
,"classpath:spring/some-common-stuff.xml"
,"classpath:spring/some-aop-stuff.xml"
,"classpath:spring/some-logging-stuff.xml"
,"classpath:spring/some-services-etc.xml"
})
public class MyCompaniesBigTestSpringConfig {
...
Create a child test class:
package x.y.z;
@ContextConfiguration
public class MyOneOffTest extends MyCompaniesBigTestSpringConfig {
...
and put in src/test/resources/x/y/z/MyOneOffTest-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="widgetsService" class="com.mycompany.mydept.myservice.WidgetsService" primary="true" />
</beans>
That widgetsService
bean will override (take the place of) the bean defined in the main config xml (or Java config). See about inheritLocations
Also Note the default -context.xml file. Example of that here.
Update: I had to add primary="true"
, apparently it's needed.