How to exclude *AutoConfiguration classes in Spring Boot JUnit tests?
Asked Answered
C

14

92

I tried:

@RunWith(SpringJUnit4ClassRunner.class)
@EnableAutoConfiguration(exclude=CrshAutoConfiguration.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class LikeControllerTest {

However the CRaSSHD still starts up. While currently it doesn't harm the test, I'd like to disable unnecessary modules during unit testing to speed up and also avoid potential conflicts.

Corselet answered 2/11, 2014 at 9:59 Comment(2)
whats a CRaSSHD supposed to be?Unlettered
With Jean-Philippe Bond's answer in mind (explaining that @SpringApplicationConfiguration prevents @EnableAutoConfiguration from taking effect), it's worth noting that in test-slice composite annotations like @DataJpaTest, we see that they use combinations of @OverrideAutoConfiguration(enabled=false) to broadly disable auto configuration and @ImportAutoConfiguration(classes...) to turn specific configuration back on. These continue to work as new auto-configuration elements are added to the application.Albinus
A
59

Top answers don't point to an even simpler and more flexible solution.

just place a

@TestPropertySource(properties=
{"spring.autoconfigure.exclude=comma.seperated.ClassNames,com.example.FooAutoConfiguration"})
@SpringBootTest
public class MySpringTest {...}

annotation above your test class. This means other tests aren't affected by the current test's special case. If there is a configuration affecting most of your tests, then consider using the spring profile instead as the current top answer suggests.

Thanks to @skirsch for encouraging me to upgrade this from a comment to an answer.

Award answered 30/1, 2019 at 2:5 Comment(3)
I need to exclude a configuration class (which is not Auto-configured) in my test case. It's a custom class annotated with @Configuration, I need to skip loading this in my test case. When I try the above spring.autoconfigure.exclude parameter, I get an error that says the class is not an auto-configured one. Any help?Subsocial
#39730252Award
This worked perfectly for me, but I did need to also use a profile to prevent the creation of the Cassandra repository beans that I was trying to avoid.Countersink
C
66

Another simple way to exclude the auto configuration classes,

Add below similar configuration to your application.yml file,

---
spring:
  profiles: test
  autoconfigure.exclude: org.springframework.boot.autoconfigure.session.SessionAutoConfiguration
Cairo answered 3/6, 2016 at 7:52 Comment(4)
Thank you for this really simple and effective solution. You saved my time :)Contrite
To add more than one auto configuration class to the property spring.autoconfigure.exclude, simply put all classes name separated by a ,Jap
@Cairo I find it easier to specify @TestPropertySource(properties= {"spring.autoconfigure.exclude=comma.seperated.ClassNames,com.example.FooAutoConfiguration"}) on the *Test class. saves having to define a profile for every permutation of undesired configs.Award
@Award you da real MVP. Please provide this as an answer, it is most helpful.Lorindalorine
A
59

Top answers don't point to an even simpler and more flexible solution.

just place a

@TestPropertySource(properties=
{"spring.autoconfigure.exclude=comma.seperated.ClassNames,com.example.FooAutoConfiguration"})
@SpringBootTest
public class MySpringTest {...}

annotation above your test class. This means other tests aren't affected by the current test's special case. If there is a configuration affecting most of your tests, then consider using the spring profile instead as the current top answer suggests.

Thanks to @skirsch for encouraging me to upgrade this from a comment to an answer.

Award answered 30/1, 2019 at 2:5 Comment(3)
I need to exclude a configuration class (which is not Auto-configured) in my test case. It's a custom class annotated with @Configuration, I need to skip loading this in my test case. When I try the above spring.autoconfigure.exclude parameter, I get an error that says the class is not an auto-configured one. Any help?Subsocial
#39730252Award
This worked perfectly for me, but I did need to also use a profile to prevent the creation of the Cassandra repository beans that I was trying to avoid.Countersink
K
28

I had a similar use case where I wanted to test a Spring Boot configured repository in isolation (in my case without Spring Security autoconfiguration which was failing my test). @SpringApplicationConfiguration uses SpringApplicationContextLoader and that has a JavaDoc stating

Can be used to test non-web features (like a repository layer) or start an fully-configured embedded servlet container.

However, like yourself, I could not work out how you are meant to configure the test to only test the repository layer using the main configuration entry point i.e. using your approach of @SpringApplicationConfiguration(classes = Application.class).

My solution was to create a completely new application context exclusive for testing. So in src/test/java I have two files in a sub-package called repo

  1. RepoIntegrationTest.java
  2. TestRepoConfig.java

where RepoIntegrationTest.java has

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestRepoConfig.class)
public class RepoIntegrationTest {

and TestRepoConfig.java has

@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class TestRepoConfig {

It got me out of trouble but it would be really useful if anyone from the Spring Boot team could provide an alternative recommended solution

Kial answered 28/3, 2015 at 16:31 Comment(3)
This looks the right solution to me. You need a slightly different configuration for testing and you define one. Maybe you don't like that a test configuration is annotated with \@SprintBootApplication. But consider that this is just a shortcut for \@Configuration \@ComponentScan \@EnableComponentScan. Those look just fine on a test configuration.Tailstock
In what packages do your Application and TestRepoConfig classes reside? Because I have a similar problem but this solution doesn't work for me. The excluded configuration classes still get included. Both my @ SpringBootApplication classes live at the root package of the app (i.e com.company.app).Preventer
In my case Application would be in com.company.app and TestRepoConfig in com.company.app.repoKial
B
13

I had a similar problem but I came to a different solution that may help others. I used Spring Profiles to separate out test and app configuration classes.

  1. Create a TestConfig class with a specific profile and exclude any app configuration from component scan you wish here.

  2. In your test class set the profile to match the TestConfig and include it using the @ContextConfiguration annotation.

For example:

configuration:

@Profile("test")
@Configuration
@EnableWebMvc
@ComponentScan(
    basePackages="your.base.package",
    excludeFilters = {
            @Filter(type = ASSIGNABLE_TYPE,
                    value = {
                            ExcludedAppConfig1.class,
                            ExcludedAppConfig2.class
            })
    })
public class TestConfig { ...}

test:

@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfig.class)
@WebAppConfiguration
public class SomeTest{ ... }
Berck answered 4/2, 2016 at 12:8 Comment(0)
G
8

I think that using the @EnableAutoConfiguration annotation on a test class won't work if you are using @SpringApplicationConfiguration to load your Application class. The thing is that you already have a @EnableAutoConfiguration annotation in the Application class that does not exclude the CrshAutoConfiguration.Spring uses that annotation instead of the one on your test class to do the auto configuration of your beans.

I think that your best bet is to use a different application context for your tests and exclude the CrshAutoConfiguration in that class.

I did some tests and it seems that @EnableAutoConfiguration on the test class is completely ignore if you are using the @SpringApplicationConfiguration annotation and the SpringJUnit4ClassRunner.

Glovsky answered 2/11, 2014 at 12:3 Comment(1)
How do you structure a project with several different applications? Won't Spring Boot scan the other Application(s) anyway? What's the recommended best practice for this scenario...? I think this is common usage, having test configuration somewhat different than application configuration.Corselet
D
7

With the new @SpringBootTest annotation, I took this answer and modified it to use profiles with a @SpringBootApplication configuration class. The @Profile annotation is necessary so that this class is only picked up during the specific integration tests that need this, as other test configurations do different component scanning.

Here is the configuration class:

@Profile("specific-profile")
@SpringBootApplication(scanBasePackages={"com.myco.package1", "com.myco.package2"})
public class SpecificTestConfig {

}

Then, the test class references this configuration class:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { SpecificTestConfig.class })
@ActiveProfiles({"specific-profile"})
public class MyTest {

}
Dearly answered 13/9, 2016 at 17:49 Comment(2)
does that really work for you? I defined only 1 package for the scanBasePackages of @SpringBootApplication, but when I run my test, it is still initializing classes from another package. The rest of the code are the same. I am using spring boot 1.4.0.RELEASEPennipennie
It is still going to my main config class with @SpringBootApplication for production. I added the profile specification there and added my config package to scanBasePackages and now it is working!Pennipennie
H
6
@SpringBootTest(classes = {Application.class}
              , webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
              , properties="spring.autoconfigure.exclude=com.xx.xx.AutoConfiguration"
               )

ref:https://github.com/spring-projects/spring-boot/issues/8579

Hotien answered 22/5, 2018 at 14:5 Comment(0)
G
3

If the issue is that your SpringBootApplication/Configuration you are bringing in is component scanning the package your test configurations are in, you can actually remove the @Configuration annotation from the test configurations and you can still use them in the @SpringBootTest annotations. For example, if you have a class Application that is your main configuration and a class TestConfiguration that is a configuration for certain, but not all tests, you can set up your classes as follows:

@Import(Application.class) //or the specific configurations you want
//(Optional) Other Annotations that will not trigger an autowire
public class TestConfiguration {
    //your custom test configuration
}

And then you can configure your tests in one of two ways:

  1. With the regular configuration:

    @SpringBootTest(classes = {Application.class}) //won't component scan your configuration because it doesn't have an autowire-able annotation
    //Other annotations here
    public class TestThatUsesNormalApplication {
        //my test code
    }
    
  2. With the test custom test configuration:

    @SpringBootTest(classes = {TestConfiguration.class}) //this still works!
    //Other annotations here
    public class TestThatUsesCustomTestConfiguration {
        //my test code
    }
    
Gillard answered 17/7, 2018 at 21:46 Comment(0)
S
2

got into same kind of problem, wasn't able to exclude main spring boot class during testing. Solved it using following approach.

Instead of using @SpringBootApplication, use all three annotations which it contains and assign the name to @Configuration

@Configuration("myApp")
@EnableAutoConfiguration
@ComponentScan
public class MyApp { .. }

In your test class define configuration with exactly same name:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
// ugly hack how to exclude main configuration
@Configuration("myApp")
@SpringApplicationConfiguration(classes = MyTest.class)
public class MyTest { ... }

This should help. Would be nice to have some better way in place how to disable auto scanning for configuration annotations...

Sermon answered 30/10, 2015 at 16:25 Comment(0)
P
1

I have struggled with a similar issue for one day... My Scenario:

I have a SpringBoot application and I use applicationContext.xml in scr/main/resources to configure all my Spring Beans. For testing(integration testing) I use another applicationContext.xml in test/resources and things worked as I have expected: Spring/SpringBoot would override applicationContext.xml from scr/main/resources and would use the one for Testing which contained the beans configured for testing.

However, just for one UnitTest I wanted yet another customization for the applicationContext.xml used in Testing, just for this Test I wanted to used some mockito beans, so I could mock and verify, and here started my one day head-ache!

The problem is that Spring/SpringBoot doesn't not override the applicationContext.xml from scr/main/resources ONLY IF the file from test/resources HAS the SAME NAME. I tried for hours to use something like:

@RunWith(SpringJUnit4ClassRunner.class)
@OverrideAutoConfiguration(enabled=true)
@ContextConfiguration({"classpath:applicationContext-test.xml"})

it did not work, Spring was first loading the beans from applicationContext.xml in scr/main/resources

My solution based on the answers here by @myroch and @Stuart:

  1. Define the main configuration of the application:

    @Configuration @ImportResource({"classpath:applicationContext.xml"}) public class MainAppConfig { }

this is used in the application

@SpringBootApplication
@Import(MainAppConfig.class)
public class SuppressionMain implements CommandLineRunner
  1. Define a TestConfiguration for the Test where you want to exclude the main configuration

    @ComponentScan( basePackages = "com.mypackage", excludeFilters = { @ComponentScan.Filter(type = ASSIGNABLE_TYPE, value = {MainAppConfig.class}) }) @EnableAutoConfiguration public class TestConfig { }

By doing this, for this Test, Spring will not load applicationContext.xml and will load only the custom configuration specific for this Test.

Pyretotherapy answered 8/10, 2018 at 7:43 Comment(0)
B
0

I struggled with this as well and found a simple pattern to isolate the test context after a cursory read of the @ComponentScan docs.

/**
* Type-safe alternative to {@link #basePackages} for specifying the packages
* to scan for annotated components. The package of each class specified will be scanned.
* Consider creating a special no-op marker class or interface in each package
* that serves no purpose other than being referenced by this attribute.
*/
Class<?>[] basePackageClasses() default {};

  1. Create a package for your spring tests, ("com.example.test").
  2. Create a marker interface in the package as a context qualifier.
  3. Provide the marker interface reference as a parameter to basePackageClasses.

Example


IsolatedTest.java

package com.example.test;

@RunWith(SpringJUnit4ClassRunner.class)
@ComponentScan(basePackageClasses = {TestDomain.class})
@SpringApplicationConfiguration(classes = IsolatedTest.Config.class)
public class IsolatedTest {

     String expected = "Read the documentation on @ComponentScan";
     String actual = "Too lazy when I can just search on Stack Overflow.";

      @Test
      public void testSomething() throws Exception {
          assertEquals(expected, actual);
      }

      @ComponentScan(basePackageClasses = {TestDomain.class})
      public static class Config {
      public static void main(String[] args) {
          SpringApplication.run(Config.class, args);
      }
    }
}

...

TestDomain.java

package com.example.test;

public interface TestDomain {
//noop marker
}
Burgher answered 27/1, 2016 at 22:12 Comment(2)
While basePackageClasses= can be used to control which configurations from our application, it has no effect on Spring Boot's own configurations.Corselet
Did you notice that I'm running a separate instance for testing? public static class Config{} ... This is an isolated instance... not even the same app...Burgher
B
0

If you're having this problem with Spring Boot 1.4.x and up, you might be able to use @OverrideAutoConfiguration(enabled=true) to solve the problem.

Similar to what was asked/answered here https://mcmap.net/q/242242/-how-to-exclude-disable-a-specific-auto-configuration-in-spring-boot-1-4-0-for-datajpatest

Bascio answered 1/3, 2017 at 6:15 Comment(0)
B
0

I think that the best solution currently for springBoot 2.0 is using profiles

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.DEFINED_PORT)
@ActiveProfiles("test")
public class ExcludeAutoConfigIntegrationTest {
    // ...
} 

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration

anyway in the following link give 6 different alternatives to solve this.

Broadus answered 21/3, 2019 at 4:24 Comment(1)
Profiles is not new in Spring Boot 2. it has existed since Spring 3 (2011) or so. And it has nothing to do with excluding auto configuration that don't use profiles, which is pretty much everyone of them.Atticism
N
0

So to disable the auto-loading of all Beans for a Test, the test class can explicitly mention the dependencies required. This can be done using ContextConfiguration annotation. eg,

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {EmployeeService.class})
public class EmployeeLeavesTest { 

   @Autowired
   private EmployeeService employeeService;

}

In this eg, only EmployeeService class will be available and other beans will not be loaded.

Nugatory answered 9/3, 2021 at 13:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.