Spring Boot: @TestConfiguration Not Overriding Bean During Integration Test

JavaSpring BootSpring Test

Java Problem Overview


I have a Bean defined in a class decorated with @Configuration:

@Configuration
public class MyBeanConfig {
    @Bean
    public String configPath() {
        return "../production/environment/path";
    }
}

I have a class decorated with @TestConfiguration that should override this Bean:

@TestConfiguration
public class MyTestConfiguration {
    @Bean
    @Primary
    public String configPath() {
        return "/test/environment/path";
    }
}

The configPath bean is used to set the path to an external file containing a registration code that must be read during startup. It is used in an @Component class:

@Component
public class MyParsingComponent {
    private String CONFIG_PATH;
    
    @Autowired
    public void setCONFIG_PATH(String configPath) {
        this.CONFIG_PATH = configPath;
    }
}

While trying to debug this I set a breakpoint inside each method as well as the constructor of the test config class. The @TestConfiguration's constructor breakpoint is hit, so i know that my test configuration class instantiates, however the configPath method of that class is never hit. Instead, the configPath method of the normal @Configuration class is hit and the @Autowired String in MyParsingComponent is always ../production/environment/path rather than the expected /test/environment/path.

Not sure why this is happening. Any thoughts would be greatly appreciated.

Java Solutions


Solution 1 - Java

As documented in the Detecting Test Configuration section of the Spring Boot reference manual, any beans configured in a top-level class annotated with @TestConfiguration will not be picked up via component scanning. So you have to explicitly register your @TestConfiguration class.

You can do that either via @Import(MyTestConfiguration.class) or @ContextConfiguration(classes = MyTestConfiguration.class) on your test class.

On the other hand, if your class annotated with @TestConfiguration were a static nested class within your test class, it would be registered automatically.

Solution 2 - Java

Make sure that the method name of your @Bean factory method does not match any existing bean name. I had issues with method names like config() or (in my case) prometheusConfig() which collided with existing bean names. Spring skips those factory methods silently and simply does not call them / does not instantiate the beans.

If you want to override a bean definition in your test, use the bean name explicitly as string parameter in your @Bean("beanName") annotation.

Solution 3 - Java

  • Test configuration has to be explicitly imported in the test via @Import({MyTestConfiguration.class}).
  • The name of the @Bean methods in @Configuration and @TestConfiguration have to be different. At least it makes difference in Spring Boot v2.2.
  • Also make sure spring.main.allow-bean-definition-overriding=true otherwise the bean could not be overriden.

Solution 4 - Java

For me worked this code:

  @TestConfiguration // 1. necessary
  public class TestMessagesConfig {

    @Bean
    @Primary // 2. necessary
    public MessageSource testMessageSource() { // 3. different method name than in production code e.g. add test prefix

    }
  }

Solution 5 - Java

I came across a similar issue recently and got it sorted out by annotating my testing bean with @Primary as well as @Bean. Not sure why it's required, which seems not documented in the Spring doc. The version of my SpringBoot is 2.0.3.

Solution 6 - Java

I struggled with a related problem, whereby even though I was using an inner static class, my test bean was not being registered.

It turns out, You still need to add your inner static class to the @ContextConfiguration class array, otherwise the beans inside the @TestConfiguration doesn't get picked up.

public interface Foo {
    String execute();
}
public class FooService {
    private final Foo foo;

    FooService(Foo foo) {
        this.foo = foo;
    }

    public String execute() {
        return foo.execute();
    }
}
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {FooService.class, FooTest.FooTestConfig.class})
public class FooTest {
    @Autowired
    FooService fooService;

    @Test
    void test() {
        Assertions.assertEquals("MY_TEST_BEAN", fooService.execute());
    }

    @TestConfiguration
    static class FooTestConfig {
        @Bean
        public Foo getFooBean() {
            return () -> "MY_TEST_BEAN";
        }
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionThe Head RushView Question on Stackoverflow
Solution 1 - JavaSam BrannenView Answer on Stackoverflow
Solution 2 - JavaMarcel ThimmView Answer on Stackoverflow
Solution 3 - JavaMaksim SorokinView Answer on Stackoverflow
Solution 4 - JavaAndrew SneckView Answer on Stackoverflow
Solution 5 - JavasnowfoxView Answer on Stackoverflow
Solution 6 - JavaDidier BreedtView Answer on Stackoverflow