Gabriel Petrovay
Gabriel Petrovay

Reputation: 21934

How can I override the Spring component scanning when running a JUnit 5 test?

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:

Upvotes: 2

Views: 2036

Answers (1)

jucosorin
jucosorin

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

Related Questions