Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpringBootTest - how to replace one bean in runtime configuration?

I am writing an integration test for Spring Boot application. All goes well as long as I'm using 100% runtime configuration for the test. But when I'm trying to provide just one custom bean for the bean, everything breaks.

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CombinedControllerIntegrationTest2 {

@TestConfiguration
static class ContextConfiguration {

    @Bean
    @Primary
    public SolrDocumentTypeMapRepository solrDocumentTypeMapRepository() {
        LOG.debug("SolrDocumentTypeMapRepository is being initialized.");

// etc.

code variant above is causing the real, runtime SolrDocumentTypeMapRepository to load. ContextConfiguration in my test class is ignored.

If I try to use @Configuration instead of @TestConfiguration on my inner ContextConfiguration, execution falls to another extreme - it ends with

org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.

Because, apparently, the rest of configuration is not loaded.

If I try to put

@ContextConfiguration(classes = {CombinedControllerIntegrationTest2.ContextConfiguration.class,GatewayApplication.class})

on my main testing class, it fails the same way as in #1 - i.e. my ContextConfiguration is ignored.

Any ideas?

P.S. I know I can use @MockBean (and this even works in other cases), but here, because at some point I have dependency on @PostConsruct methods in the main code, @MockBeans are useless.

like image 483
Alexander Avatar asked Oct 02 '17 02:10

Alexander


People also ask

How do you override a bean in a Spring boot?

Bean Overriding Spring beans are identified by their names within an ApplicationContext. Therefore, bean overriding is a default behavior that happens when we define a bean within an ApplicationContext that has the same name as another bean. It works by simply replacing the former bean in case of a name conflict.

Can we create a bean at runtime?

This can be done without restarting the application at runtime when Loading and Removing bean in Spring Application. If the client code needs to register objects which are not managed by Spring container, then we will need to work with an instance of BeanDefinition. Here, We have used the following dependencies.

Is @SpringBootApplication a bean?

Annotation Type SpringBootApplication. Indicates a configuration class that declares one or more @Bean methods and also triggers auto-configuration and component scanning . This is a convenience annotation that is equivalent to declaring @Configuration , @EnableAutoConfiguration and @ComponentScan .

Does @configuration creates a bean?

Annotating a class with the @Configuration annotation indicates that the class will be used by JavaConfig as a source of bean definitions. An application may make use of just one @Configuration -annotated class, or many. @Configuration can be considered the equivalent of XML's <beans/> element.


1 Answers

Unit Test with One Bean

Just use @RunWith(SpringRunner.class) annotation, it should work. You can also use @RunWith(SpringJUnit4ClassRunner.class). Both should work.

Please don't use @SpringBootTest annotation. It will wire up the whole application.

Here is your updated example,

@RunWith(SpringRunner.class)
public class CombinedControllerIntegrationTest2 {

    @TestConfiguration
    static class ContextConfiguration {

        @Bean
        public SolrDocumentTypeMapRepository solrDocumentTypeMapRepository() {
            LOG.debug("SolrDocumentTypeMapRepository is being initialized.");
            return new SolrDocumentTypeMapRepository(...);
        }
    }

    @Autowired
    private SolrDocumentTypeMapRepository repository;

    @Test
    public void test() {
        assertNotNull(repository);
    }
}

Integration Test with a Replaced Bean

  • Create a new test Spring Boot application. It should exclude the configuration class (for e.g., SolrConfiguration) which is responsible for creating SolrDocumentTypeMapRepository bean.

    @SpringBootApplication
    @ComponentScan(basePackages = {
            "com.abc.pkg1",
            "com.abc.pk2"},
            excludeFilters = {
                    @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, 
                    value = SolrConfiguration.class)})
    public class TestApplication {
        public static void main(String[] args) throws Exception {
            SpringApplication.run(TestApplication.class, args);
        }
    }
    
  • Now, use the @ContextConfiguration annotation in your test class to add the TestApplication.class and the ContextConfiguration.class. This will wire up your application with the all the required beans including the replaced bean. Shown below is the updated test class,

    @ActiveProfiles("test")
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest(webEnvironment = 
        SpringBootTest.WebEnvironment.RANDOM_PORT)
    @ContextConfiguration(classes = {TestApplication.class, 
        CombinedControllerIntegrationTest2.ContextConfiguration.class})
    public class CombinedControllerIntegrationTest2 {
    
        @TestConfiguration
        static class ContextConfiguration {
    
            @Bean
            public SolrDocumentTypeMapRepository solrDocumentTypeMapRepository() {
                LOG.debug("SolrDocumentTypeMapRepository is being initialized.");
                return new SolrDocumentTypeMapRepository(...);
            }
        }
    
        ...
    }
    
like image 192
Indra Basak Avatar answered Oct 06 '22 00:10

Indra Basak