[[testcontext-ctx-management-ctx-hierarchies]]
= Context Hierarchies

When writing integration tests that rely on a loaded Spring `ApplicationContext`, it is
often sufficient to test against a single context. However, there are times when it is
beneficial or even necessary to test against a hierarchy of `ApplicationContext`
instances. For example, if you are developing a Spring MVC web application, you typically
have a root `WebApplicationContext` loaded by Spring's `ContextLoaderListener` and a
child `WebApplicationContext` loaded by Spring's `DispatcherServlet`. This results in a
parent-child context hierarchy where shared components and infrastructure configuration
are declared in the root context and consumed in the child context by web-specific
components. Another use case can be found in Spring Batch applications, where you often
have a parent context that provides configuration for shared batch infrastructure and a
child context for the configuration of a specific batch job.

You can write integration tests that use context hierarchies by declaring context
configuration with the `@ContextHierarchy` annotation, either on an individual test class
or within a test class hierarchy. If a context hierarchy is declared on multiple classes
within a test class hierarchy, you can also merge or override the context configuration
for a specific, named level in the context hierarchy. When merging configuration for a
given level in the hierarchy, the configuration resource type (that is, XML configuration
files or component classes) must be consistent. Otherwise, it is perfectly acceptable to
have different levels in a context hierarchy configured using different resource types.

[NOTE]
====
If you use `@DirtiesContext` in a test whose context is configured as part of a context
hierarchy, you can use the `hierarchyMode` flag to control how the context cache is
cleared.

For further details, see the discussion of `@DirtiesContext` in
xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[Spring Testing Annotations]
and the {spring-framework-api}/test/annotation/DirtiesContext.html[`@DirtiesContext`] javadoc.
====

The JUnit Jupiter based examples in this section show common configuration scenarios for
integration tests that require the use of context hierarchies.

**Single test class with context hierarchy**
--
`ControllerIntegrationTests` represents a typical integration testing scenario for a
Spring MVC web application by declaring a context hierarchy that consists of two levels,
one for the root `WebApplicationContext` (loaded by using the `TestAppConfig`
`@Configuration` class) and one for the dispatcher servlet `WebApplicationContext`
(loaded by using the `WebConfig` `@Configuration` class). The `WebApplicationContext`
that is autowired into the test instance is the one for the child context (that is, the
lowest context in the hierarchy). The following listing shows this configuration scenario:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@ExtendWith(SpringExtension.class)
	@WebAppConfiguration
	@ContextHierarchy({
		@ContextConfiguration(classes = TestAppConfig.class),
		@ContextConfiguration(classes = WebConfig.class)
	})
	class ControllerIntegrationTests {

		@Autowired
		WebApplicationContext wac;

		// ...
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@ExtendWith(SpringExtension::class)
	@WebAppConfiguration
	@ContextHierarchy(
		ContextConfiguration(classes = [TestAppConfig::class]),
		ContextConfiguration(classes = [WebConfig::class]))
	class ControllerIntegrationTests {

		@Autowired
		lateinit var wac: WebApplicationContext

		// ...
	}
----
======
--

**Class hierarchy with implicit parent context**
--
The test classes in this example define a context hierarchy within a test class
hierarchy. `AbstractWebTests` declares the configuration for a root
`WebApplicationContext` in a Spring-powered web application. Note, however, that
`AbstractWebTests` does not declare `@ContextHierarchy`. Consequently, subclasses of
`AbstractWebTests` can optionally participate in a context hierarchy or follow the
standard semantics for `@ContextConfiguration`. `SoapWebServiceTests` and
`RestWebServiceTests` both extend `AbstractWebTests` and define a context hierarchy by
using `@ContextHierarchy`. The result is that three application contexts are loaded (one
for each declaration of `@ContextConfiguration`), and the application context loaded
based on the configuration in `AbstractWebTests` is set as the parent context for each of
the contexts loaded for the concrete subclasses. The following listing shows this
configuration scenario:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@ExtendWith(SpringExtension.class)
	@WebAppConfiguration
	@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
	public abstract class AbstractWebTests {}

	@ContextHierarchy(@ContextConfiguration("/spring/soap-ws-config.xml"))
	public class SoapWebServiceTests extends AbstractWebTests {}

	@ContextHierarchy(@ContextConfiguration("/spring/rest-ws-config.xml"))
	public class RestWebServiceTests extends AbstractWebTests {}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@ExtendWith(SpringExtension::class)
	@WebAppConfiguration
	@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
	abstract class AbstractWebTests

	@ContextHierarchy(ContextConfiguration("/spring/soap-ws-config.xml"))
	class SoapWebServiceTests : AbstractWebTests()

	@ContextHierarchy(ContextConfiguration("/spring/rest-ws-config.xml"))
	class RestWebServiceTests : AbstractWebTests()

----
======
--

**Class hierarchy with merged context hierarchy configuration**
--
The classes in this example show the use of named hierarchy levels in order to merge the
configuration for specific levels in a context hierarchy. `BaseTests` defines two levels
in the hierarchy, `parent` and `child`. `ExtendedTests` extends `BaseTests` and instructs
the Spring TestContext Framework to merge the context configuration for the `child`
hierarchy level, by ensuring that the names declared in the `name` attribute in
`@ContextConfiguration` are both `child`. The result is that three application contexts
are loaded: one for `/app-config.xml`, one for `/user-config.xml`, and one for
`{"/user-config.xml", "/order-config.xml"}`. As with the previous example, the
application context loaded from `/app-config.xml` is set as the parent context for the
contexts loaded from `/user-config.xml` and `{"/user-config.xml", "/order-config.xml"}`.
The following listing shows this configuration scenario:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@ExtendWith(SpringExtension.class)
	@ContextHierarchy({
		@ContextConfiguration(name = "parent", locations = "/app-config.xml"),
		@ContextConfiguration(name = "child", locations = "/user-config.xml")
	})
	class BaseTests {}

	@ContextHierarchy(
		@ContextConfiguration(name = "child", locations = "/order-config.xml")
	)
	class ExtendedTests extends BaseTests {}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@ExtendWith(SpringExtension::class)
	@ContextHierarchy(
		ContextConfiguration(name = "parent", locations = ["/app-config.xml"]),
		ContextConfiguration(name = "child", locations = ["/user-config.xml"]))
	open class BaseTests {}

	@ContextHierarchy(
		ContextConfiguration(name = "child", locations = ["/order-config.xml"])
	)
	class ExtendedTests : BaseTests() {}
----
======
--

**Class hierarchy with overridden context hierarchy configuration**
--
In contrast to the previous example, this example demonstrates how to override the
configuration for a given named level in a context hierarchy by setting the
`inheritLocations` flag in `@ContextConfiguration` to `false`. Consequently, the
application context for `ExtendedTests` is loaded only from `/test-user-config.xml` and
has its parent set to the context loaded from `/app-config.xml`. The following listing
shows this configuration scenario:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@ExtendWith(SpringExtension.class)
	@ContextHierarchy({
		@ContextConfiguration(name = "parent", locations = "/app-config.xml"),
		@ContextConfiguration(name = "child", locations = "/user-config.xml")
	})
	class BaseTests {}

	@ContextHierarchy(
		@ContextConfiguration(
			name = "child",
			locations = "/test-user-config.xml",
			inheritLocations = false
	))
	class ExtendedTests extends BaseTests {}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@ExtendWith(SpringExtension::class)
	@ContextHierarchy(
		ContextConfiguration(name = "parent", locations = ["/app-config.xml"]),
		ContextConfiguration(name = "child", locations = ["/user-config.xml"]))
	open class BaseTests {}

	@ContextHierarchy(
			ContextConfiguration(
					name = "child",
					locations = ["/test-user-config.xml"],
					inheritLocations = false
			))
	class ExtendedTests : BaseTests() {}
----
======
--

[[testcontext-ctx-management-ctx-hierarchies-with-bean-overrides]]
**Context hierarchies with bean overrides**
--
When `@ContextHierarchy` is used in conjunction with
xref:testing/testcontext-framework/bean-overriding.adoc[bean overrides] such as
`@TestBean`, `@MockitoBean`, or `@MockitoSpyBean`, it may be desirable or necessary to
have the override applied to a single level in the context hierarchy. To achieve that,
the bean override must specify a context name that matches a name configured via the
`name` attribute in `@ContextConfiguration`.

The following test class configures the name of the second hierarchy level to be
`"user-config"` and simultaneously specifies that the `UserService` should be wrapped in
a Mockito spy in the context named `"user-config"`. Consequently, Spring will only
attempt to create the spy in the `"user-config"` context and will not attempt to create
the spy in the parent context.

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@ExtendWith(SpringExtension.class)
	@ContextHierarchy({
		@ContextConfiguration(classes = AppConfig.class),
		@ContextConfiguration(classes = UserConfig.class, name = "user-config")
	})
	class IntegrationTests {

		@MockitoSpyBean(contextName = "user-config")
		UserService userService;

		// ...
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@ExtendWith(SpringExtension::class)
	@ContextHierarchy(
		ContextConfiguration(classes = [AppConfig::class]),
		ContextConfiguration(classes = [UserConfig::class], name = "user-config"))
	class IntegrationTests {

		@MockitoSpyBean(contextName = "user-config")
		lateinit var userService: UserService

		// ...
	}
----
======

When applying bean overrides in different levels of the context hierarchy, you may need
to have all of the bean override instances injected into the test class in order to
interact with them — for example, to configure stubbing for mocks. However, `@Autowired`
will always inject a matching bean found in the lowest level of the context hierarchy.
Thus, to inject bean override instances from specific levels in the context hierarchy,
you need to annotate fields with appropriate bean override annotations and configure the
name of the context level.

The following test class configures the names of the hierarchy levels to be `"parent"`
and `"child"`. It also declares two `PropertyService` fields that are configured to
create or replace `PropertyService` beans with Mockito mocks in the respective contexts,
named `"parent"` and `"child"`. Consequently, the mock from the `"parent"` context will
be injected into the `propertyServiceInParent` field, and the mock from the `"child"`
context will be injected into the `propertyServiceInChild` field.

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@ExtendWith(SpringExtension.class)
	@ContextHierarchy({
		@ContextConfiguration(classes = ParentConfig.class, name = "parent"),
		@ContextConfiguration(classes = ChildConfig.class, name = "child")
	})
	class IntegrationTests {

		@MockitoBean(contextName = "parent")
		PropertyService propertyServiceInParent;

		@MockitoBean(contextName = "child")
		PropertyService propertyServiceInChild;

		// ...
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@ExtendWith(SpringExtension::class)
	@ContextHierarchy(
		ContextConfiguration(classes = [ParentConfig::class], name = "parent"),
		ContextConfiguration(classes = [ChildConfig::class], name = "child"))
	class IntegrationTests {

		@MockitoBean(contextName = "parent")
		lateinit var propertyServiceInParent: PropertyService

		@MockitoBean(contextName = "child")
		lateinit var propertyServiceInChild: PropertyService

		// ...
	}
----
======
--
