5

I have the following setup in a maven project. Configuration class for the productive code:

package com.example;
@Configuration
public class MyConfiguration {
    @Bean
    public A beanA() {
      return new A();
    }
    ...
}

Then I have one test, that has an internal Configuration:

package com.example;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {MyConfiguration.class, SpringConfigurationTest.MyTestConfiguration.class})
public class SpringConfigurationTest {
    @TestConfiguration
    static class MyTestConfiguration {
        @Bean
        @Primary
        public A beanA() {
          return mock(A.class);
        }    
    }
}

The tests in this class work fine. Then I do have another Test class in sub package:

package com.example.sub;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {MyConfiguration.class, AnotherSpringConfigurationTest.MyTestConfiguration.class})
public class AnotherSpringConfigurationTest {
    @TestConfiguration
    static class MyTestConfiguration {
        @Bean
        public B beanB() {
          return new B()
        }    
    }
}

When running tests in this class the test configuration from SpringConfigurationTest.MyTestConfiguration is also included. My guess the reason for this is the inclusion of MyConfiguration which lies in the root directory. The effect is that in AnotherSpringConfigurationTest the bean A is mocked instead of a real instance.

How can I avoid that configuration classes inside other tests are 'leaked' into other tests?

I have seen Spring-boot default profile for integration tests which uses Spring profiles in Spring Boot 1.4.1, while I am using Spring Boot 2.0.1. Though I am sure it could be done.

Another approach I could think of would be to use component scanning to pick up the contexts and exclude all those that I do not want, but that is a bit cumbersome, as I would much prefer an approach where I define what I want to use instead of what should not be used.

Is there an easy and elegant solution with Spring Boot 2 to avoid conflicting context configurations?

hotzst
  • 7,238
  • 9
  • 41
  • 64
  • what tells you that `SpringConfigurationTest.MyTestConfiguration` is also included ? Any errors ? – GabLeg May 23 '18 at 17:27
  • Log output states that bean `A` is overridden based on `SpringConfigurationTest.MyTestConfiguration` and when debugging I can see that it is a mock instead of the real thing. – hotzst May 23 '18 at 18:14
  • How do you retrieve Beans in your test ? With `@Autowired` ? Right now, I see 2 Beans `A` and you should have had a `NoUniqueBeanDefinitionException`. So how do you do ? – GabLeg May 23 '18 at 18:55
  • This example is maybe oversimplified, so in these occasions, I add the @Primary as suggested in your answer. However in `AnotherSpringConfigurationTest`, I would expect to have only one bean definition present and that being the one from `MyConfiguration` – hotzst May 23 '18 at 19:18

1 Answers1

1

First, I don't like the idea of putting a config class in another class. I don't know if that is frenquently made, I'm kinda new to Spring. But here is what I would do :

First config file :

@Configuration
@Import(MyConfiguration.class)
public class MyTestConfiguration {
    @Bean
    @Primary // added to let know on @Autowired to use this one instead of the one in first MyConfiguration.class
    public A beanA() {
      return mock(A.class);
    }    
}

Second config file :

@Configuration
@Import(MyConfiguration.class)
public class MyOtherTestConfiguration {
    @Bean
    public B beanB() {
      return new B()
    }    
}

I'm not familiar with @TestConfiguration. That's why i'm using @Configuration.

First test class :

package com.example;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = MyTestConfiguration.class)
public class SpringConfigurationTest {

}

Second test class :

package com.example.sub;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = MyOtherTestConfiguration.class)
public class AnotherSpringConfigurationTest {

}
GabLeg
  • 352
  • 4
  • 14
  • I had the configurations in a separate file, but that did not change a thing. – hotzst May 23 '18 at 18:12
  • It's not that it will not work, I just don't like the concept. – GabLeg May 23 '18 at 18:17
  • Tried your approach with having one configuration and bringing in the others with `@Import`. Did not work, I assume because Spring boot just does not work that way, but takes the configuration as root to scan for further beans, which configurations are one kind of. – hotzst May 24 '18 at 18:47
  • I tried the setup you provided and it worked with no problem. So I don't think the problem is on your test setup. – GabLeg May 24 '18 at 18:59
  • It doesn't work for me. The real bean is always loaded and it says "There is already...". I think the best way is still using @Profile. – emeraldhieu Nov 08 '22 at 07:41