19

I am having a SimpleTest :

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SimpleTestConfig.class)
public class SimpleTest {
    @Test
    public void test() {
        assertThat(true);
    }
}

and a configuration for this test :

@SpringBootApplication
@ComponentScan(basePackageClasses = {
        SimpleTestConfig.class,
        Application.class
},
        excludeFilters = @ComponentScan.Filter(
                type = FilterType.ASSIGNABLE_TYPE,
                classes = Starter.class))
public class SimpleTestConfig {
}

I am trying to exclude the Starter class

package application.starters;

import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

@Component
public class Starter {
    @PostConstruct
    public void init(){
        System.out.println("initializing");
    }
}

And the Application class looks like this :

package application;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import static org.springframework.boot.SpringApplication.run;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        run(Application.class, args);
    }
}

But for a very weird reason the Starter class is still getting initialized.

Can anyone explain why the ComponentScan excludeFilters is not excluding my Starter class ?

whatamidoingwithmylife
  • 1,119
  • 1
  • 16
  • 35
Daniel Taub
  • 5,133
  • 7
  • 42
  • 72
  • Either I'm missing the main (method) in SimpleTestConfig, or you do not explained well where do you want to exclude from – whoopdedoo Jan 07 '18 at 02:36
  • Replace `@SpringBootApplication` with `@Configuration` on you `SimpleTestConfig` class. And also hope it in your src/main folder, if you are creating a test purpose configuration(in src/test folder), use `@TestConfiguration` annotation instead. – Hantsy Jan 07 '18 at 04:03
  • Similar question: https://stackoverflow.com/questions/25550494/componentscan-excludefilters-not-working-in-spring-4-0-6-release – tkruse Jan 07 '18 at 04:07
  • Rwlated: https://stackoverflow.com/questions/18992880/exclude-component-from-componentscan – tkruse Jan 07 '18 at 04:35

2 Answers2

29

Each component scan does filtering individually. While you exclude Starter.class from SimpleTestConfig, SimpleTestConfig initializes Application, which does it's own @ComponentScan without excluding Starter. The clean way of using ComponentScan is for each ComponentScan to scan separate packages, that way each filter works fine. When 2 separate ComponentScans scan the same package (as in your tests), this does not work.

One way to trick this is to provide a mock Starter bean:

import org.springframework.boot.test.mock.mockito.MockBean;

public class SimpleTest {
    @MockBean
    private Starter myTestBean;
    ...
}

Spring will use that mock instead of the real class, thus the @PostConstruct method will not be called.

Other common solutions:

  • Do not directly use Application.class in any unit test
  • Use Spring profile and annotations such as @Profile("!TEST") on the Starter class
  • Use a spring Boot @ConditionalOn... annotation on the Starter class
tkruse
  • 10,222
  • 7
  • 53
  • 80
  • I am very appreciate you answer, but if that’s right, so why is the filter designed in Spring? , what’s is purpose? – Daniel Taub Jan 12 '18 at 09:12
  • The purpose is anything you want it to use for. It is very useful when a testConfig does a componentScan without also scanning another config that has it's own componentScan for similar packages (like Application in your case). – tkruse Jan 12 '18 at 09:16
  • another option is to use `@TypeExcludeFilters` annotation: https://stackoverflow.com/a/59815772/355438 – Ilya Serbis Oct 02 '20 at 00:12
  • @Lu55: Feel free to modify my answer to include that option. – tkruse Oct 02 '20 at 02:34
3

You can define custom component scan filter for excluding it.

Example code will be like:

@SpringBootApplication()
@ComponentScan(excludeFilters=@Filter(type = FilterType.REGEX, pattern="com.wyn.applications.starter.Starter*"))
public class SimpleTestConfig {

}

This works for me.

For further reading go to this blog.

Ilya Serbis
  • 21,149
  • 6
  • 87
  • 74
Yogi
  • 1,805
  • 13
  • 24