Reputation: 557
I have a two classes which are in a parent-child relation, the second one being a mock of the first.
public class A {
public void doSomething(){...};
}
public class MockA extends A {
@Override
public void doSomething() {...};
}
I also have two @Configuration classes one for development environment and one for test, the test one just mocks a couple of behaviors, but it imports the development one. Either way, I want that in test environment for MockA to be injected and in development for class A to be injected in other services which autowire it.
I can do that if I overwrite the bean in the test configuration. The following will work:
@Configuration
public class ApplicationConfig {
@Bean
public A beanA() {
return new A();
}
}
@Configuration
public class TestApplicationConfig {
@Bean
public A beanA() {
return new MockA();
}
}
However, I do not want to create the beans in the @Configuration class. I want to put the @Component annotation on each one and let them be injected in services correctly.
I've tried two approaches
1) Creating a dummy annotation, adding the annotation on the class A and trying to exclude the bean from the TestApplicationConfig.
@Configuration
@ComponentScan(
basePackages = {
"murex.connectivity.tools.interfaces.monitor.cellcomputer",
"murex.connectivity.tools.interfaces.monitor"
}, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes =
MyAnnotation.class))
public class TestApplicationConfig {
}
2) using @Component and @Primary annotations on the MockA class. My logic being that in the case both are present (which will happen only on the test case, because only then the MockA is scanned), the MockA will be injected everywhere. But this does not happen.
I am wondering if I am doing something wrong or is this a limitation from Spring? The @Primary annotation seems to be constructed exactly for this specific case, am I mistaken? Is the fact that the two classes have a parent-child relationship that is the problem?
L.E. Using two different profiles will work. I am more curious if my understanding of the two presented approaches is correct and/or if this is a limitation on Spring using @Component annotations
Upvotes: 0
Views: 1990
Reputation: 2293
Tried to combine two suggested approaches. It worked for me:
@Component
@Primary
public class ClassA {
public void doSomething() {
System.out.println("A");
}
}
@Component
public class ClassB extends ClassA {
@Override
public void doSomething() {
System.out.println("B");
}
}
@Component
public class ClassC {
@Autowired
public ClassA component;
}
All three classes are in same package.
@Configuration
@ComponentScan(value = "com.test", excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes =
Primary.class))
public class RandomConfig {
}
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {RandomConfig.class})
public class RandomTest {
@Autowired
ClassC c;
@Test
void when_then() {
//prints "B"
c.component.doSomething();
}
}
So RandomConfig
excludes all @Primary
beans, whereas production config uses only @Primary
Upvotes: 1
Reputation: 198
You can use profiles.
@Profile("!test")
@Configuration
public class ApplicationConfig {
@Bean
public A beanA() {
return new A();
}
}
And for test cases:
in test resources in application.properties: spring.profiles.active=test
or on test class @ActiveProfiles("test")
and create configuration:
@Profile("test")
@Configuration
public class TestApplicationConfig {
@Bean
public A beanA() {
return new MockA();
}
}
Upvotes: 1