Overriding an Autowired Bean in Unit Tests

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.

91010 次浏览

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

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

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
}
}

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

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.

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.