Reputation: 21934
I have this JUnit 5 test:
@SpringBootTest
public class MyTest {
...
}
The application default configuration loads many @Component
's @Service
's and @Configuration
's.
For some tests, I would like to tell the Spring application running in this JUnit test context to not scan all the components and filter out some heavy loading or verbose/spammy components that might complain while running in a (mocked) environment where not all the requirements are met.
I tried to provide the @ComponentScan annotation to MyTest
class in the form below. I added on purpose multiple filters and multiple types being filtered with the hope that I see less beans being loaded/registered in the application context:
@ComponentScan(
useDefaultFilters = false,
excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {
org.springframework.context.annotation.Configuration.class,
org.springframework.stereotype.Service.class,
org.springframework.stereotype.Component.class
}),
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {
MyRabbitMqListener.class
})
}
)
@SpringBootTest
public class MyTest {
@Autowired
private ApplicationContext context;
@Test
public void expectingLessRegisteredBeans() {
List<String> beans = Arrays.stream(context.getBeanDefinitionNames())
// ...
.collect(Collectors.toList());
assertEquals(beans.size(), ...);
}
}
Regardless what I provide to the @CompoenntScan
, I don't manage to control the amount or what beans are being scanned/registered in the JUnit 5 Spring test context.
But I still want to use the @SpringBootTest
in order to get most of my application's configuration but exclude some parts of it (because they are slow, or spammy in the logs, or just throwing errors). For example, an application that receives event from inputs like Kafka, RabbitMQ, or REST, processes them and saves them to the persistence layer. I want to test the processing and persistence integration. I throw a bunch of test events to the processor @Service
and then expect to see the results in the DB via the @Repository
.
What would be my alternatives:
application-*.properties
to mute some logging (the non-test code needs to be polluted with this test feature, and tedious creating multiple profiles)Upvotes: 2
Views: 2036
Reputation: 1
You can use a org.springframework.boot.test.context.filter.TypeExcludeFilter
.
On your @SpringBootTest
annotated integration test add a @TypeExcludeFilters(YourCustomTypeExcludeFilter.class)
annotation and it will use your implemented TypeExcludeFilter
to filter out beans you don't want from the test's ApplicationContext.
@SpringBootTest
@TypeExcludeFilters(YourCustomTypeExcludeFilter.class)
public class MyTest {
@Autowired
private ApplicationContext context;
@Test
public void expectingLessRegisteredBeans() {
List<String> beans = Arrays.stream(context.getBeanDefinitionNames())
// ...
.collect(Collectors.toList());
assertEquals(beans.size(), ...);
}
}
A simple TypeExcludeFilter
that excludes beans by class name is the one below:
public class YourCustomTypeExcludeFilter extends TypeExcludeFilter {
private static final List<String> beanNamesToExclude = List.of(
"org.personal.MyRabbitMqListener");
@Override
public boolean equals(Object obj) {
return (obj != null) && (getClass() == obj.getClass());
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {
return beanNamesToExclude.stream()
.anyMatch(beanName -> metadataReader.getClassMetadata().getClassName().equals(beanName));
}
}
Sky's the limit on the logic you can use with the match()
method.
For some more inspiration of what is possible just search for classes that extend TypeExcludeFilter
in the Spring Boot source code (for instance look at org.springframework.boot.test.context.filter.TestTypeExcludeFilter
, a filter that's used to exclude @TestConfiguration
annotated classes)
Upvotes: 0