Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inner static class with @Configuration picked up by spring scanner for all tests. What is wrong?

Spring picks up inner @Configuration for Test1 from Test2. I need a mocked IService in Test2 but a real ServiceImpl in Test1. Also I want to have common TestConfiguration for all my tests. But I always have mocked IService in both tests. What is wrong?

How I can disable inner configurations picking up for sibling tests?

Here is my code :

ServiceImpl.java:

@Service
public class SeriviveImpl implements IService {
}

TestConfiguration.java:

@Configuration
@ComponentScan
public class TestConfiguration {
   // empty
}

Test1.java:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestConfiguration.class})
public class Test1 {
    @Autowired
    private IService service;
}

Test2.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {Test2.CustomConfiguration.class, TestConfiguration.class})
public class Test2 {
    @Autowired
    private IService service;

    @Configuration
    static class CustomConfiguration {
        @Bean
        IService service() {
            return mock(IService.class);
        }
    }
}
like image 1000
Fire Avatar asked Sep 21 '17 12:09

Fire


2 Answers

You can filter the inner class from the TestConfiguration @ComponentScan:

@Configuration
@ComponentScan(excludeFilters = {
     @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
                           value = Test2.CustomConfiguration.class)
})
public class TestConfiguration {
   // empty
}

This will prevent it being picked up in Test1.

EDIT Or if you have lots of inner configuration you can create your own annotation and filter all these annotated classes from your @ComponentScan:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
public @interface InnerConfiguration {
}

Then use this annotation on your inner classes instead of @Configuration:

@InnerConfiguration
    static class CustomConfiguration {
        @Bean
        IService service() {
            return mock(IService.class);
        }
    }

and filter these out of your component scan like this:

@Configuration
@ComponentScan(excludeFilters = {
     @ComponentScan.Filter(type = FilterType.ANNOTATION,
                           value = InnerConfiguration.class)
})
public class TestConfiguration {
   // empty
}
like image 185
Plog Avatar answered Nov 08 '22 00:11

Plog


Since you explicitly use Test2.CustomConfiguration.class in Test2's @ContextConfiguration annotation, you can remove @Configuration annotation from Test2.CustomConfiguration and it will not be picked up by @ComponentScan during Test1 run.

This works because:

To load an ApplicationContext for your tests by using annotated classes (see Java-based container configuration), you can annotate your test class with @ContextConfiguration and configure the classes attribute with an array that contains references to annotated classes.

and

The term “annotated class” can refer to any of the following:

A class annotated with @Configuration.

A component (that is, a class annotated with @Component, @Service, @Repository, or other stereotype annotations).

A JSR-330 compliant class that is annotated with javax.inject annotations.

Any other class that contains @Bean methods.

See https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-ctx-management-javaconfig

like image 3
AlexanderYastrebov Avatar answered Nov 08 '22 02:11

AlexanderYastrebov