Kirill Tolkachev
Kirill Tolkachev

Reputation: 123

Spontaneous up spring boot context in test

I have an issue with spring-boot-test during deep dive in spring TestContext configuration.

Example project: github example project

I have three Spring configurations with some beans (Config1 - heavyweight config, and I need to cache it)

I use @ContextHierarchy for separate configs in the context, and I expect that Config1 will be loaded only once. (Used @ContextHierarchy because I did not find an analogue in spring-boot-test)

Full code:

@ContextHierarchy({
    @ContextConfiguration(classes = {Config1.class}),
    @ContextConfiguration(classes = {Config2.class}),
})

If I run the test, the Config1 was really cached! However, I see the following:

Started ConfigTest1

Two times And

Started ConfigTest2

Only once. In my opinion, it is possible because ConfigTest1 already cached as TestContext (it happens, because ConfigTest1 and ConfigTest2 contained in the same package)

That means spring boot up my spring context three times! Why? See spring boot logo three times in log:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.2.RELEASE)

What do you mean about this behaviour? Is it a bug or a feature? Maybe I do something wrong?

Update1:

Thanks a lot. However, if i set properties (or classes) in @SpringBootTest in different order in two test classes – no configuration will be cached :(

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextHierarchy({
    @ContextConfiguration(classes = {Config1.class}),
    @ContextConfiguration(classes = {Config2.class}),
})
public class ConfigTest1 {

and

@RunWith(SpringRunner.class)
@SpringBootTest(properties = "property1=1")
@ContextHierarchy({
    @ContextConfiguration(classes = {Config1.class}),
    @ContextConfiguration(classes = {Config3.class}),
})
public class ConfigTest2 {

In case there is no properties – config1 really will be cached! And in this case – nothing cached

Upvotes: 2

Views: 3694

Answers (1)

Sam Brannen
Sam Brannen

Reputation: 31177

Everything is working as it should!

You're just confused because of the fact that Spring Boot logs the name of the test class for which the ApplicationContext was loaded. Thus, you see ConfigTest1 twice in the log, because two contexts are loaded for that test class.

If you add logging.level.org.springframework.test.context.cache=debug to application.properties, you'll see the following log output.

DEBUG ... c.DefaultCacheAwareContextLoaderDelegate : Storing ApplicationContext in cache under key [[WebMergedContextConfiguration@7a765367 testClass = ConfigTest1, locations = '{}', classes = '{class spring.test.mistake.delete.Config1}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.SpringBootTestContextCustomizer@643b1d11, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6d00a15d, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@475530b9], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]]
DEBUG ... c.DefaultCacheAwareContextLoaderDelegate : Storing ApplicationContext in cache under key [[WebMergedContextConfiguration@52feb982 testClass = ConfigTest1, locations = '{}', classes = '{class spring.test.mistake.delete.Config2}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.SpringBootTestContextCustomizer@643b1d11, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6d00a15d, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@475530b9], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [WebMergedContextConfiguration@7a765367 testClass = ConfigTest1, locations = '{}', classes = '{class spring.test.mistake.delete.Config1}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.SpringBootTestContextCustomizer@643b1d11, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6d00a15d, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@475530b9], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]]]
DEBUG ... c.DefaultCacheAwareContextLoaderDelegate : Storing ApplicationContext in cache under key [[WebMergedContextConfiguration@5ddcc487 testClass = ConfigTest2, locations = '{}', classes = '{class spring.test.mistake.delete.Config3}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.SpringBootTestContextCustomizer@643b1d11, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6d00a15d, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@475530b9], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [WebMergedContextConfiguration@44c73c26 testClass = ConfigTest2, locations = '{}', classes = '{class spring.test.mistake.delete.Config1}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.SpringBootTestContextCustomizer@643b1d11, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6d00a15d, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@475530b9], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]]]
DEBUG ... org.springframework.test.context.cache   : Spring test ApplicationContext cache statistics: [DefaultContextCache@478db956 size = 3, maxSize = 32, parentContextCount = 1, hitCount = 11, missCount = 3]

Thus, there are in fact only three contexts loaded, namely the three you expect.

Upvotes: 1

Related Questions