4

In my Spring boot 2.1 project I have different @Configurations for different test (ConfigurationA and ConfigurationB), that reside in different packages. Both configurations define the same set of beans but in a different manner (mocked vs. the real thing)

As I am aware of the Bean overriding mechanism introduced in Spring Boot 2.1, I have set the property: spring.main.allow-bean-definition-overriding=true.

However I do have a test with the following the setup of the following configuration and test class. First there is a @Configuration in the productive part (I'm using Maven):

package com.stackoverflow;

@Configuration
public class ProdConfiguration{
...
}

Then in the test branch there is a general Test @Configuration on the same package level:

package com.stackoverflow

@Configuration
public class TestConfiguration {
  @Bean
  public GameMap gameMap() {
    return Mockito.mock(GameMap.class);
  }
}

And in a subpackage I have another @Configuration:

package com.stackoverflow.impl;

@Configuration
public class RealMapTestConfiguration {
  @Bean
  public GameMap gameMap() {
    return new GameMap("testMap.json");
  }
}

And then of course there is the test that is troubling me:

package com.stackoverflow.impl;

@ExtendWith(SpringExtension.class)
@SpringBootTest
@ContextConfiguration(classes={RealMapTestConfiguration.class, ProdConfiguration.class})
@ActiveProfiles("bug") // spring.main.allow-bean-definition-overriding=true
public class MapImageServiceIT {
  @Autowired
  private GameMap map;
} 

It turns out that the injected GameMap into my test is a mock instance from TestConfiguration instead of the real thing from RealMapTestConfiguration. Aparrently in my test I have the configuration from ProdConfiguration and TestConfiguration, when I wanted ProdConfiguration and RealMapTestConfiguration. As the beans defined in the ProdConfiguration and *TestConfiguration are different the combination works, but TestConfiguration and RealMapTestConfiguration define the same been. It seems like the TestConfiguration is picked up by component scanning as it is in the same package as ProdConfiguration. I was under the impression that when overriding beans the bean definition that is closer to the test class would be preferred. However this seems not to be the case.

So here are my questions:

  1. When overriding beans, what is the order? Which bean overrides which one?
  2. How to go about to get the correct instance in my test (using a different bean name is not an option, as in reality the injected bean is not directly used in the test but in a service the test uses and there is no qualifier on it.)
2
  • Did you try using a dedicated integration-testing-profile? Also, since what you return from the configuration is a mock anyway, why don't you use @MockBean in your test class? Commented Dec 9, 2018 at 7:08
  • @DoeJohnson: Injecting the bean as MockBean solved the issues, as I could then define the mocked behaviour in the setup and only use one TestConfiguration. Commented Dec 9, 2018 at 7:36

3 Answers 3

1

I've not used the spring.main.allow-bean-definition-overriding=true property, but specifying specific config in a test class has worked fine for me as a way of switching between objects in different tests.

You say...

It turns out that the injected GameMap into my test is a mock instance from TestConfiguration instead of the real thing from RealMapTestConfiguration.

But RealMapTestConfiguration does return a mock

package com.stackoverflow.impl;

@Configuration
public class RealMapTestConfiguration {
  @Bean
  public GameMap gameMap() {
    return Mockito.mock(GameMap.class);
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

The code examples were the wrong way round: TestConfiguration returns a mock instance and RealMapTestConfiguration returns a real instance.
1

I think the problem here is that including ContextConfiguration nullifies (part of) the effect of @SpringBootTest. @SpringBootTest has the effect of looking for @SpringBootConfiguration in your application (starting from the same package, I believe). However, if ContextConfiguration is applied, then configurations are loaded from there.

Another way of saying that: because you have ContextConfiguration in your test, scanning for @Configuration classes is disabled, and TestConfiguration is not loaded.

I don't think I have a full picture of your configuration setup so can't really recommend a best practice here, but a quick way to fix this is to add TestConfiguration to your ContextConfiguration in your test. Make sure you add it last, so that it overrides the bean definitions in the other two configurations.

The other thing that might work is removing @ContextConfiguration entirely and letting the SpringBootApplication scanning do its thing - that's where what you said about the bean definition that is closest may apply.

2 Comments

Clarified question. Placing the two configuration files on @SpringBootTest and remove @ContextConfiguration makes no difference, as TestConfiguration is picked up by package scanning and that configuration should not be included.
Thanks for the clarification. I believe specifying the classes using @SpringBootTest has the same effect as using ContextConfiguration - you can set debug=true in your application.properties to see which configuration Spring is using. Either way, sounds like @MockBean does what you want to, too.
1

In that case just don't use @Configuration on configuration class and import it to the test manually using @Import, example:

@SpringBootTest
@Import(MyTest.MyTestConfig.class)
public class MyTest {

    @Autowired
    private String string;

    @Test
    public void myTest() {
        System.out.println(string);
    }

    static class MyTestConfig {
        @Bean
        public String string() {
            return "String";
        }
    }
}

2 Comments

This worked for me.. thanks a lot. I had @TestConfiguration on my test mock classes and that was interfering with the main application.
We can mark the @TestConfiguration as primary, so that test cases will consider this as primary bean and won't complain.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.