@ContextHierarchy Example in Spring Test
November 21, 2019
This page will walk through @ContextHierarchy
annotation example in Spring test. The @ContextHierarchy
is used at class level in integration test classes to define a hierarchy of ApplicationContext
instances. The @ContextHierarchy
defines a list of one or more @ContextConfiguration
instances which defines a level in the context hierarchy. The @ContextHierarchy
can be used for single test class as well as in test class hierarchy. Context can be merged and overridden using named context in context hierarchy.
Find the sample code to use
@ContextHierarchy
in integration test class using JavaConfig.
@ExtendWith(SpringExtension.class) @ContextHierarchy({ @ContextConfiguration(classes = AppConfig1.class), @ContextConfiguration(classes = AppConfig2.class), @ContextConfiguration(classes = AppConfig3.class) }) public class MyAppTest { ------ }
@ExtendWith(SpringExtension.class) @ContextHierarchy({ @ContextConfiguration("app-config1.xml"), @ContextConfiguration("app-config2.xml"), @ContextConfiguration("app-config3.xml") }) public class MyAppTest { ------ }
Now let us discuss using
@ContextHierarchy
in our integration test classes in detail.
Contents
Technologies Used
Find the technologies being used in our example.1. Java 11
2. Spring 5.2.0.RELEASE
3. Spring Boot 2.2.0.RELEASE
4. JUnit 5.3.2
5. Maven 3.5.2
6. Eclipse 2018-09
Java Configurations
Find the Java configurations being used in our test application.AppConfig1.java
package com.concretepage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig1 { @Bean("myBeanA") public String myBeanA() { return "My Bean A"; } }
package com.concretepage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig2 { @Bean("myBeanB") public String myBeanB() { return "My Bean B"; } }
package com.concretepage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig3 { @Bean("myBeanC") public String myBeanC() { return "My Bean C"; } }
package com.concretepage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig4 { @Bean("myBeanD") public String myBeanC() { return "My Bean D"; } }
@ContextHierarchy in Single Test Class
The@ContextHierarchy
can be used in a single integration test class. In following test class, there are three context hierarchies. The context created by AppConfig1
is the parent context created by AppConfig2
and in the same way this context will be the parent of AppConfig3
. Parent child relation of context will be as following.
context(AppConfig1) | context(AppConfig2) | context(AppConfig3)
MyAppTest1.java
package com.concretepage; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextHierarchy; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @ContextHierarchy({ @ContextConfiguration(classes = AppConfig1.class), @ContextConfiguration(classes = AppConfig2.class), @ContextConfiguration(classes = AppConfig3.class) }) public class MyAppTest1 { @Autowired private ApplicationContext context; @Test public void testMethod1() { //Test result is SUCCESS assertEquals(true, context.containsBean("myBeanA")); assertEquals(true, context.containsBean("myBeanB")); assertEquals(true, context.containsBean("myBeanC")); printAllBeans(); } void printAllBeans() { ApplicationContext ctx = context; while(ctx != null) { System.out.println("---------"); List.of(ctx.getBeanNamesForType(String.class)) .forEach(s -> System.out.println(s)); ctx = ctx.getParent(); } } }
--------- myBeanC --------- myBeanB --------- myBeanA
@ContextHierarchy in Class Hierarchy with Implicit Parent Context
In a class hierarchy, the@ContextHierarchy
configures the context of parent test class as parent context for the context of child test class. Here we are creating an example in which we have parent class as MyAppBaseTest
and it is extended by child test class MyAppTest2
. The context in MyAppBaseTest
is configured by AppConfig1
and the context in MyAppTest2
is configured by {AppConfig2, AppConfig3}
. Find the parent child relation of context in our example.
context(AppConfig1) | context({AppConfig2, AppConfig3})
MyAppBaseTest.java
package com.concretepage; import java.util.List; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = AppConfig1.class) public class MyAppBaseTest { @Autowired protected ApplicationContext context; void printAllBeans() { ApplicationContext ctx = context; while(ctx != null) { System.out.println("---------"); List.of(ctx.getBeanNamesForType(String.class)) .forEach(s -> System.out.println(s)); ctx = ctx.getParent(); } } }
package com.concretepage; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextHierarchy; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @ContextHierarchy({ @ContextConfiguration(classes = {AppConfig2.class, AppConfig3.class}) }) public class MyAppTest2 extends MyAppBaseTest { @Test public void testMethod1() { //Test result is SUCCESS assertEquals(true, context.containsBean("myBeanA")); assertEquals(true, context.containsBean("myBeanB")); assertEquals(true, context.containsBean("myBeanC")); printAllBeans(); } }
--------- myBeanB myBeanC --------- myBeanA
@ContextHierarchy in Class Hierarchy with Merged Context
In a context hierarchy, we can merge context configurations for specific levels using named hierarchy levels. In our example we have a base test class asAppBaseTest
which has named context configurations with name parent
for AppConfig1
and child
for AppConfig2
. Now we will create a test class MyAppTest3
extending AppBaseTest
. In MyAppTest3
we are using same named context configuration i.e. child
for AppConfig3
. So in MyAppTest3
, the context of AppConfig2
and AppConfig3
will be merged. Find the parent child relation of context in our example.
context(AppConfig1) | context({AppConfig2, AppConfig3}) | context(AppConfig4)
AppBaseTest.java
package com.concretepage; import java.util.List; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextHierarchy; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @ContextHierarchy({ @ContextConfiguration(name = "parent", classes = AppConfig1.class), @ContextConfiguration(name = "child", classes = AppConfig2.class) }) public class AppBaseTest { @Autowired protected ApplicationContext context; void printAllBeans() { ApplicationContext ctx = context; while(ctx != null) { System.out.println("---------"); List.of(ctx.getBeanNamesForType(String.class)) .forEach(s -> System.out.println(s)); ctx = ctx.getParent(); } } }
package com.concretepage; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextHierarchy; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @ContextHierarchy({ @ContextConfiguration(name = "child", classes = AppConfig3.class), @ContextConfiguration(classes = AppConfig4.class) }) public class MyAppTest3 extends AppBaseTest { @Test public void testMethod1() { //Test result is SUCCESS assertEquals(true, context.containsBean("myBeanA")); assertEquals(true, context.containsBean("myBeanB")); assertEquals(true, context.containsBean("myBeanC")); assertEquals(true, context.containsBean("myBeanD")); printAllBeans(); } }
--------- myBeanD --------- myBeanB myBeanC --------- myBeanA
@ContextHierarchy in Class Hierarchy with Overridden Context
The named context in the context hierarchy configured by@ContextHierarchy
can be overridden by setting inheritLocations
element of @ContextConfiguration
as false
. In our example we have following parent child relation of context.
context(AppConfig1) | context(AppConfig3) | context(AppConfig4)
MyAppTest4.java
package com.concretepage; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextHierarchy; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @ContextHierarchy({ @ContextConfiguration(name = "child", inheritLocations = false, classes = AppConfig3.class), @ContextConfiguration(classes = AppConfig4.class) }) public class MyAppTest4 extends AppBaseTest { @Test public void testMethod1() { //Test result is SUCCESS assertEquals(true, context.containsBean("myBeanA")); assertEquals(false, context.containsBean("myBeanB")); assertEquals(true, context.containsBean("myBeanC")); assertEquals(true, context.containsBean("myBeanD")); printAllBeans(); } }
--------- myBeanD --------- myBeanC --------- myBeanA
References
Spring testing: @ContextHierarchySpring doc: @ContextHierarchy