Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use @ComponentScan together with test-specific ContextConfigurations in SpringJunit4TestRunner?

Tags:

I am testing a Spring Boot application. I have several test classes, each of which needs a different set of mocked or otherwise customized beans.

Here is a sketch of the setup:

src/main/java:

package com.example.myapp;  @SpringBootApplication @ComponentScan(         basePackageClasses = {                 MyApplication.class,                 ImportantConfigurationFromSomeLibrary.class,                 ImportantConfigurationFromAnotherLibrary.class}) @EnableFeignClients @EnableHystrix public class MyApplication {     public static void main(String[] args) {         SpringApplication.run(MyApplication.class, args);     } }  package com.example.myapp.feature1;  @Component public class Component1 {     @Autowired     ServiceClient serviceClient;      @Autowired     SpringDataJpaRepository dbRepository;      @Autowired     ThingFromSomeLibrary importantThingIDontWantToExplicitlyConstructInTests;      // methods I want to test... } 

src/test/java:

package com.example.myapp;  @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = MyApplication.class) @WebAppConfiguration @ActiveProfiles("test") public class Component1TestWithFakeCommunication {      @Autowired     Component1 component1; // <-- the thing we're testing. wants the above mock implementations of beans wired into it.      @Autowired     ServiceClient mockedServiceClient;      @Configuration     static class ContextConfiguration {         @Bean         @Primary         public ServiceClient mockedServiceClient() {             return mock(ServiceClient.class);         }     }      @Before     public void setup() {         reset(mockedServiceClient);     }      @Test     public void shouldBehaveACertainWay() {         // customize mock, call component methods, assert results...     } }  package com.example.myapp;  @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = MyApplication.class) @WebAppConfiguration @ActiveProfiles("test") public class Component1TestWithRealCommunication {      @Autowired     Component1 component1; // <-- the thing we're testing. wants the real implementations in this test.      @Autowired     ServiceClient mockedServiceClient;      @Before     public void setup() {         reset(mockedServiceClient);     }      @Test     public void shouldBehaveACertainWay() {         // call component methods, assert results...     } } 

The problem with the above setup is that the component scan configured in MyApplication picks up Component1TestWithFakeCommunication.ContextConfiguration, so I get a mock ServiceClient even in Component1TestWithRealCommunication where I want the real ServiceClient implementation.

Although I could use @Autowired constructors and build up the components myself in both tests, there is a sufficient amount of stuff with complicated setup that I would rather have Spring TestContext set up for me (for example, Spring Data JPA repositories, components from libraries outside the app that pull beans from the Spring context, etc.). Nesting a Spring configuration inside the test that can locally override certain bean definitions within the Spring context feels like it should be a clean way to do this; the only downfall is that these nested configurations end up affecting all Spring TestContext tests that base their configuration on MyApplication (which component scans the app package).

How do I modify my setup so I still get a "mostly real" Spring context for my tests with just a few locally overridden beans in each test class?

like image 675
Jonathan Fuerth Avatar asked Sep 02 '16 20:09

Jonathan Fuerth


People also ask

What is the use of @ComponentScan annotation?

The @ComponentScan annotation is used with the @Configuration annotation to tell Spring the packages to scan for annotated components. @ComponentScan also used to specify base packages and base package classes using thebasePackageClasses or basePackages attributes of @ComponentScan.

What is the difference between @component and ComponentScan?

@Component and @ComponentScan are for different purposes. @Component indicates that a class might be a candidate for creating a bean. It's like putting a hand up. @ComponentScan is searching packages for Components.

What is the use of @ComponentScan annotation in Spring boot?

One of the most important annotations in spring is @ComponentScan which is used along with the @Configuration annotation to specify the packages that we want to be scanned. @ComponentScan without arguments tells Spring to scan the current package and all of its sub-packages.

Why do we use @ComponentScan?

@ComponentScan tells Spring in which packages you have annotated classes which should be managed by Spring. Spring needs to know which packages contain spring beans, otherwise you would have to register each bean individually in(xml file). This is the use of @ComponentScan.


1 Answers

The following should help you to achieve your goal by introducing a new fake-communication profile that is applicable only to the current test class.

@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = MyApplication.class) @WebAppConfiguration @ActiveProfiles({"test", "fake-communication"}) public class Component1TestWithFakeCommunication {      // @Autowired ...      @Profile("fake-communication")     @Configuration     static class ContextConfiguration {         @Bean         @Primary         public ServiceClient mockedServiceClient() {             return mock(ServiceClient.class);         }     } } 
like image 192
Sam Brannen Avatar answered Sep 19 '22 11:09

Sam Brannen