Let’s dig deeper into configurations for integration tests in Spring. We’ll start with a nested integration context example, for a class (that might be a test class, because that’s our main focus). While we can use @Import, if we set up one-time configuration, it could be nested in the test class. Here’s an example:
@RunWith(SpringRunner.class) @SpringBootTest public class OurControllerTests{ @Autowired OurController controller; @Autowired OurDependency mockDependency; @Configuration @Import(ControllerConfiguration.class) static class NestedConfiguration { @Bean public OurDependency mockDependency() { OurDependency mock = Mockito.mock(OurDependency.class); when(mock.isActive()).thenReturn(true); return mock; } } .... }
Let’s take a look at what we have here. Our test class, and code-under-test will inject two @Autowired objects. The first one is the controller. Its @Bean is configured in another configuration class, called ControllerConfiguration. This configuration is imported by the nested configuration in the test class. The NestedConfiguration class declares a bean that creates and initializes a mock, which is probably a different implementation from the main app implementation. Also, the reason for using a one-time configuration is having the initialization different than other configurations, used by other test classes. We need to remember a couple of things of how Spring works:
- The nested configuration class has to be static. While usually not a problem, it loads differently than regular configuration classes.
- If we use an @ContextConfiguration on the test class (not @Import on the nested configuration class, as in the example), it will override the nested configuration. That can be confusing and can cause hilarious (or disastrous) consequences.
The cool thing about a nested configuration is that it’s right there in the test class that needs it, so no need to look for it elsewhere. That’s a readability plus. However, nested configurations are not re-usable, and we would probably want to reuse a single configuration for multiple test classes. So, unless nested configurations are your default way of working, it’s probably better to use non-nested ones. They should be used in very special circumstances for very special initialization.
Next, we’ll look at managing and organizing configurations for production and tests.
You want to know more about Spring Testing, don’t you? Check out the Spring API Testing workshop and my book “Everyday Spring Testing“.
2 Comments
avenue · September 20, 2024 at 12:46 pm
>If we use an @ContextConfiguration on the test class (not @Import on the nested configuration class, as in the example), it will override the nested configuration. That can be confusing and can cause hilarious (or disastrous) consequences.
So is that mean you cannot have configuration classes inside test classes annotated with @ContextConfiguration ?
I’m using Spock instead of JUnit so I cannot @ExtendWith nor @RunWith annotations so I’m using @ContextConfiguration to create ApplicationContext in my tests.
Also I don’t use Spring Boot but Spring Framework so I guess I shouldn’t use @SpringBootTest.
This stackoverflow answer(https://stackoverflow.com/a/57491956/17697751) implies that it is possible though…
Any advices are appreciated.
Gil Zilberfeld · September 21, 2024 at 11:02 am
As the SO example shows, you can always go from annotations to manual calls to create the application context. So anything you want loaded, can be done manually. The annotations help load beans automatically, but if that doesn’t work for you, there’s always the manual way.