0

How to override @Configuation which is present under src/main/java with @TestConfiguration during unit tests?

@Configuration
public class AppConfig {
    

    @Bean
    public EmployeeService employeeService(){
        return new EmployeeService();
    }


}


@Component
public class ServerStartSetup implements CommandLineRunner {
    

    @Autowired
    private EmployeeService employeeService;

    public void run(String... args) {
        // do something with employee service
    }

}

I would like to override the above bean with some below custom bean for testing purposes.

@TestConfiguration
public class TestAppConfig {
    
    @Bean
    public EmployeeService employeeService(){
        return new FakeEmployeeService();
    }

}


@SpringBootTest
@Import(TestAppConfig.class)
public class UnitTest {
    



}

However AppConfig does not seem to be skipped. That is , it throws an error saying that there is a bean with same name employeeService. If I rename bean method name in the TestAppConfig, it injects the bean created via AppConfig.

How to fix this.?

Note: One possible solution is using @Profile. I am looking for anything other than using Profiles.

KitKarson
  • 5,211
  • 10
  • 51
  • 73
  • See [documentation of `@TestConfiguration`](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/context/TestConfiguration.html): "@Configuration that can be used to define additional beans or customizations for a test. Unlike regular `@Configuration` classes the use of `@TestConfiguration` does not prevent auto-detection of `@SpringBootConfiguration`.". Since it is meant to define additional stuff only for testing, if you want to override existing beans, you might have better luck with `@Configuration` instead. – eis Jan 02 '22 at 16:57
  • Also, see [this answer](https://stackoverflow.com/a/54688203/365237) – eis Jan 02 '22 at 17:01
  • Does this answer your question? [Spring Boot: @TestConfiguration Not Overriding Bean During Integration Test](https://stackoverflow.com/questions/50607285/spring-boot-testconfiguration-not-overriding-bean-during-integration-test) – eis Jan 02 '22 at 17:01
  • @eis, No it does not. Note that I am already using `@Import`the answer suggested in the other question. You should remove your close vote. – KitKarson Jan 02 '22 at 19:04
  • you are using Import, but you are not using the suggested naming in the answer I linked from that thread. Please read the thread, not just the accepted answer. ("If you want to override a bean definition in your test, use the bean name explicitly as string parameter in your @Bean("beanName") annotation.") – eis Jan 02 '22 at 20:20
  • I saw that too. It would not help at all. I tried. it throws an error 2 beans found. – KitKarson Jan 02 '22 at 21:05
  • 1
    so it did help? along with "I had to add @Primary as well. Otherwise it would just throw expected single matching bean but found 2"-change? – eis Jan 02 '22 at 23:17
  • Understood. Thanks. – KitKarson Jan 02 '22 at 23:56
  • @eis, Thanks for being patient and clarifying. I upvoted your couple of your other answers. – KitKarson Jan 03 '22 at 01:19

3 Answers3

0

I tested locally and found that changing the method name or @Bean to @Bean("fakeEmployeeService") and adding the @Primary annotation works.

@SpringBootTest
class DemoApplicationTests {

    @Autowired
    private EmployeeService employeeService;


    @TestConfiguration
    static class TestConfig {

        //@Bean("fakeEmployeeService")
        @Bean
        @Primary
        public EmployeeService employeeServiceTest() {
            return new EmployeeService() {
                @Override
                public void doSomething() {
                    System.out.println("Do something from test...");
                }
            };
        }
    }
    ...

}
Kaz
  • 358
  • 3
  • 11
0

If we want to override a bean definition in @TestConfiguration, we need:

  1. To use the same name as the overridden bean. (Otherwise it would be an "additional" bean and we could get conflict/'d have to qualify/primary)
  2. Since spring-boot:2.1: spring.main.allow-bean-definition-overriding=true (set this in tests ONLY!plz!)

#ref

Then, with:

@TestConfiguration
public class TestAppConfig {
    
    @Bean // when same name, no @Primary needed
    public EmployeeService employeeService(){ // same name as main bean!
        return new FakeEmployeeService();
    }

}

We can do that:

@Import(TestAppConfig.class)
@SpringBootTest(properties = "spring.main.allow-bean-definition-overriding=true")
public class UnitTest {
  ... // EmployeeService will be "fake", the rest is from "main config"
xerx593
  • 12,237
  • 5
  • 33
  • 64
-1

You can mock the AppConfig bean in your test like this:

 @MockBean
 private AppConfig config;

Or, like you said, just use profiles.

Alex
  • 16
  • 3