ChatGPT解决这个技术问题 Extra ChatGPT

Override a single @Configuration class on every spring boot @Test

On my spring boot application I want to override just one of my @Configuration classes with a test configuration (in particular my @EnableAuthorizationServer @Configuration class), on all of my tests.

So far after an overview of spring boot testing features and spring integration testing features no straightforward solution has surfaced:

@TestConfiguration: It's for extending, not overriding;

@ContextConfiguration(classes=…) and @SpringApplicationConfiguration(classes =…) let me override the whole config, not just the one class;

An inner @Configuration class inside a @Test is suggested to override the default configuration, but no example is provided;

Any suggestions?


a
alexbt

Inner test configuration

Example of an inner @Configuration for your test:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SomeTest {

    @Configuration
    static class ContextConfiguration {
        @Bean
        @Primary //may omit this if this is the only SomeBean defined/visible
        public SomeBean someBean () {
            return new SomeBean();
        }
    }

    @Autowired
    private SomeBean someBean;

    @Test
    public void testMethod() {
        // test
    }
}

Reusable test configuration

If you wish to reuse the Test Configuration for multiple tests, you may define a standalone Configuration class with a Spring Profile @Profile("test"). Then, have your test class activate the profile with @ActiveProfiles("test"). See complete code:

@RunWith(SpringRunner.class)
@SpringBootTests
@ActiveProfiles("test")
public class SomeTest {

    @Autowired
    private SomeBean someBean;

    @Test
    public void testMethod() {
        // test
    }
}

@Configuration
@Profile("test")
public class TestConfiguration {
    @Bean
    @Primary //may omit this if this is the only SomeBean defined/visible
    public SomeBean someBean() {
        return new SomeBean();
    }
}

@Primary

The @Primary annotation on the bean definition is to ensure that this one will have priority if more than one are found.


Thanks. I've noticed I can also override the class on all tests by simply dropping the overriding @EnableAuthorizationServer @Configuration class on src/test/java. Spring boot rules :-)
As a sidenote, if you just need to change one value on a particular @Configuration, instead of overriding it all you can enable a specific spring boot profile (e.g. named test) for your tests using the annotation @ActiveProfiles({"test", ...}) on your test class(es). Then a simple if (Arrays.asList(environment.getActiveProfiles()).contains("test")) on your @Configuration will do.
Your solution fails if one of the other beans use internally, it's injected by, the SomeBean class. In order to make it work just add the ContextConfiguration class in the list of classes used by the SpringBootTest annotation. That is: @SpringBootTest(classes = {Application.class, SomeTest.ContextConfiguration.class})
The "reusable" part does not work. In my example, despite placing @Primary on a bean in test-config, Spring overrides it with a bean from main-config. I.e. it does overriding, just not the way you expect it - chooses the wrong bean.
@Marksim Yes, I had the same problem. If you use @TestConfiguration.(instead of @Configuration) like author mentioned, the Beans get picked up right. And I would prefere @Imort over @SpringBootTest(classes=...), somehow I got wired behaivor in some cases where a Test with an extra @Import worked and another without not. This hole Spring configuration for testing takes a lot of time to understand. puh
S
Slava Babin

You should use spring boot profiles:

Annotate your test configuration with @Profile("test"). Annotate your production configuration with @Profile("production"). Set production profile in your properties file: spring.profiles.active=production. Set test profile in your test class with @Profile("test").

So when your application starts it will use "production" class and when test stars it will use "test" class.

If you use inner/nested @Configuration class it will be be used instead of a your application’s primary configuration.


T
Tamas Hegedus

I recently had to create a dev version of our application, that should run with dev active profile out of the box without any command line args. I solved it with adding this one class as a new entry, which sets the active profile programmatically:

package ...;

import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;

@Import(OriginalApplication.class)
public class DevelopmentApplication {
    public static void main(String[] args) {
        SpringApplication application =
            new SpringApplication(DevelopmentApplication.class);
        ConfigurableEnvironment environment = new StandardEnvironment();
        environment.setActiveProfiles("dev");
        application.setEnvironment(environment);
        application.run(args);
    }
}

See Spring Boot Profiles Example by Arvind Rai for more details.