此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

上下文层次结构

在编写依赖于加载的 Spring 的集成测试时ApplicationContext是的 通常足以针对单个上下文进行测试。但是,有时是 有益甚至有必要针对ApplicationContext实例。例如,如果您正在开发 Spring MVC Web 应用程序,您通常 有根WebApplicationContext由 Spring 的ContextLoaderListener和 孩子WebApplicationContext由 Spring 的DispatcherServlet.这导致 父子上下文层次结构,其中共享组件和基础结构配置 在根上下文中声明,并由特定于 Web 的子上下文中使用 组件。另一个用例可以在 Spring Batch 应用程序中找到,您经常在其中 具有为共享批处理基础结构提供配置的父上下文和 用于配置特定批处理作业的子上下文。spring-doc.cadn.net.cn

您可以通过声明上下文来编写使用上下文层次结构的集成测试 配置将@ContextHierarchy注释,无论是在单个测试类上 或在测试类层次结构中。如果在多个类上声明了上下文层次结构 在测试类层次结构中,还可以合并或覆盖上下文配置 对于上下文层次结构中的特定命名级别。合并配置时 给定级别,配置资源类型(即 XML 配置 文件或组件类)必须一致。否则,完全可以接受 在使用不同资源类型配置的上下文层次结构中具有不同的级别。spring-doc.cadn.net.cn

如果您使用@DirtiesContext在上下文配置为上下文一部分的测试中 层次结构中,您可以使用hierarchyMode标志来控制上下文缓存的方式 清除。spring-doc.cadn.net.cn

有关更多详细信息,请参阅@DirtiesContextSpring Testing Annotations@DirtiesContextjavadoc 的文档。spring-doc.cadn.net.cn

本节中基于 JUnit Jupiter 的示例显示了 需要使用上下文层次结构的集成测试。spring-doc.cadn.net.cn

具有上下文层次结构的单个测试类spring-doc.cadn.net.cn

ControllerIntegrationTests表示 Spring MVC Web 应用程序通过声明由两个级别组成的上下文层次结构, 一个用于根WebApplicationContext(通过使用TestAppConfig @Configuration类),一个用于调度程序 servletWebApplicationContext(通过使用WebConfig @Configuration类)。这WebApplicationContext自动连接到测试实例的 is 是子上下文的 (即 层次结构中的最低上下文)。以下列表显示了此配置方案:spring-doc.cadn.net.cn

@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextHierarchy({
	@ContextConfiguration(classes = TestAppConfig.class),
	@ContextConfiguration(classes = WebConfig.class)
})
class ControllerIntegrationTests {

	@Autowired
	WebApplicationContext wac;

	// ...
}
@ExtendWith(SpringExtension::class)
@WebAppConfiguration
@ContextHierarchy(
	ContextConfiguration(classes = [TestAppConfig::class]),
	ContextConfiguration(classes = [WebConfig::class]))
class ControllerIntegrationTests {

	@Autowired
	lateinit var wac: WebApplicationContext

	// ...
}

具有隐式父上下文的类层次结构spring-doc.cadn.net.cn

此示例中的测试类在测试类中定义上下文层次结构 等级制度。AbstractWebTests声明根的配置WebApplicationContext在 Spring 驱动的 Web 应用程序中。但请注意,AbstractWebTests不声明@ContextHierarchy.因此,子类AbstractWebTests可以选择参与上下文层次结构或遵循 标准语义@ContextConfiguration.SoapWebServiceTestsRestWebServiceTests两者都延伸AbstractWebTests并通过以下方式定义上下文层次结构 用@ContextHierarchy.结果是加载了三个应用程序上下文(一个 对于每个声明@ContextConfiguration),以及加载的应用程序上下文 基于中的配置AbstractWebTests被设置为每个 为具体子类加载的上下文。以下列表显示了这一点 配置场景:spring-doc.cadn.net.cn

@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 {}
@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()

具有合并上下文层次结构配置的类层次结构spring-doc.cadn.net.cn

此示例中的类展示了使用命名层次结构级别来合并 上下文层次结构中特定级别的配置。BaseTests定义两个级别 在层次结构中,parentchild.ExtendedTests延伸BaseTests并指示Spring TestContext 框架合并child层次结构级别,通过确保在name属性@ContextConfiguration都是child. 结果是加载了三个应用程序上下文:一个用于/app-config.xml,一个用于/user-config.xml,一个用于{"/user-config.xml", "/order-config.xml"}. 与前面的示例一样,从应用程序上下文加载自/app-config.xml被设置为从中加载的上下文/user-config.xml{"/user-config.xml", "/order-config.xml"}. 以下列表显示了此配置方案:spring-doc.cadn.net.cn

@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 {}
@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() {}

具有覆盖上下文层次结构配置的类层次结构spring-doc.cadn.net.cn

与前面的示例相反,此示例演示了如何覆盖配置,方法是将inheritLocations标记@ContextConfigurationfalse. 因此,应用程序上下文的ExtendedTests仅从/test-user-config.xml和 将其父级设置为从以下位置加载的上下文/app-config.xml. 以下列表显示了此配置方案:spring-doc.cadn.net.cn

@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 {}
@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() {}

具有 bean 覆盖的上下文层次结构spring-doc.cadn.net.cn

什么时候@ContextHierarchybean 覆盖结合使用,例如@TestBean,@MockitoBean@MockitoSpyBean,则可能需要或有必要将覆盖应用于上下文层次结构中的单个级别。为此,bean 覆盖必须指定一个上下文名称,该名称与通过name属性@ContextConfiguration.spring-doc.cadn.net.cn

以下测试类将第二个层次结构级别的名称配置为"user-config"并同时指定UserService应该包裹在 上下文中的 Mockito 间谍名为"user-config".因此,Spring只会 尝试在"user-config"上下文,并且不会尝试创建 父上下文中的间谍。spring-doc.cadn.net.cn

@ExtendWith(SpringExtension.class)
@ContextHierarchy({
	@ContextConfiguration(classes = AppConfig.class),
	@ContextConfiguration(classes = UserConfig.class, name = "user-config")
})
class IntegrationTests {

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

	// ...
}
@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

	// ...
}

在上下文层次结构的不同级别中应用 bean 覆盖时,您可能需要将所有 bean 覆盖实例注入到测试类中,以便与它们交互——例如,为模拟配置存根。 然而@Autowired将始终注入在上下文层次结构的最低级别中找到的匹配 bean。因此,要从上下文层次结构中的特定级别注入 bean 覆盖实例,您需要使用适当的 bean 覆盖注释来注释字段,并配置上下文级别的name。spring-doc.cadn.net.cn

以下测试类将层次结构级别的名称配置为"parent""child".它还声明了两个PropertyService配置为 创建或替换PropertyServicebean 在各自的上下文中与 Mockito 模拟, 叫"parent""child".因此,来自"parent"上下文将 被注入propertyServiceInParent字段,以及来自"child"context 将被注入到propertyServiceInChild田。spring-doc.cadn.net.cn

@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;

	// ...
}
@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

	// ...
}