此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10! |
上下文层次结构
在编写依赖于加载的 Spring 的集成测试时ApplicationContext
是的
通常足以针对单个上下文进行测试。但是,有时是
有益甚至有必要针对ApplicationContext
实例。例如,如果您正在开发 Spring MVC Web 应用程序,您通常
有根WebApplicationContext
由 Spring 的ContextLoaderListener
和
孩子WebApplicationContext
由 Spring 的DispatcherServlet
.这导致
父子上下文层次结构,其中共享组件和基础结构配置
在根上下文中声明,并由特定于 Web 的子上下文中使用
组件。另一个用例可以在 Spring Batch 应用程序中找到,您经常在其中
具有为共享批处理基础结构提供配置的父上下文和
用于配置特定批处理作业的子上下文。
您可以通过声明上下文来编写使用上下文层次结构的集成测试
配置将@ContextHierarchy
注释,无论是在单个测试类上
或在测试类层次结构中。如果在多个类上声明了上下文层次结构
在测试类层次结构中,还可以合并或覆盖上下文配置
对于上下文层次结构中的特定命名级别。合并配置时
给定级别,配置资源类型(即 XML 配置
文件或组件类)必须一致。否则,完全可以接受
在使用不同资源类型配置的上下文层次结构中具有不同的级别。
如果您使用 有关更多详细信息,请参阅 |
本节中基于 JUnit Jupiter 的示例显示了 需要使用上下文层次结构的集成测试。
具有上下文层次结构的单个测试类
ControllerIntegrationTests
表示
Spring MVC Web 应用程序通过声明由两个级别组成的上下文层次结构,
一个用于根WebApplicationContext
(通过使用TestAppConfig
@Configuration
类),一个用于调度程序 servletWebApplicationContext
(通过使用WebConfig
@Configuration
类)。这WebApplicationContext
自动连接到测试实例的 is 是子上下文的 (即
层次结构中的最低上下文)。以下列表显示了此配置方案:
-
Java
-
Kotlin
@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
// ...
}
具有隐式父上下文的类层次结构
此示例中的测试类在测试类中定义上下文层次结构
等级制度。AbstractWebTests
声明根的配置WebApplicationContext
在 Spring 驱动的 Web 应用程序中。但请注意,AbstractWebTests
不声明@ContextHierarchy
.因此,子类AbstractWebTests
可以选择参与上下文层次结构或遵循
标准语义@ContextConfiguration
.SoapWebServiceTests
和RestWebServiceTests
两者都延伸AbstractWebTests
并通过以下方式定义上下文层次结构
用@ContextHierarchy
.结果是加载了三个应用程序上下文(一个
对于每个声明@ContextConfiguration
),以及加载的应用程序上下文
基于中的配置AbstractWebTests
被设置为每个
为具体子类加载的上下文。以下列表显示了这一点
配置场景:
-
Java
-
Kotlin
@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()
具有合并上下文层次结构配置的类层次结构
此示例中的类展示了使用命名层次结构级别来合并
上下文层次结构中特定级别的配置。BaseTests
定义两个级别
在层次结构中,parent
和child
.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"}
. 以下列表显示了此配置方案:
-
Java
-
Kotlin
@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() {}
具有覆盖上下文层次结构配置的类层次结构
与前面的示例相反,此示例演示了如何覆盖配置,方法是将inheritLocations
标记@ContextConfiguration
自false
. 因此,应用程序上下文的ExtendedTests
仅从/test-user-config.xml
和 将其父级设置为从以下位置加载的上下文/app-config.xml
. 以下列表显示了此配置方案:
-
Java
-
Kotlin
@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 覆盖的上下文层次结构
什么时候@ContextHierarchy
与 bean 覆盖结合使用,例如@TestBean
,@MockitoBean
或@MockitoSpyBean
,则可能需要或有必要将覆盖应用于上下文层次结构中的单个级别。为此,bean 覆盖必须指定一个上下文名称,该名称与通过name
属性@ContextConfiguration
.
以下测试类将第二个层次结构级别的名称配置为"user-config"
并同时指定UserService
应该包裹在
上下文中的 Mockito 间谍名为"user-config"
.因此,Spring只会
尝试在"user-config"
上下文,并且不会尝试创建
父上下文中的间谍。
-
Java
-
Kotlin
@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。
以下测试类将层次结构级别的名称配置为"parent"
和"child"
.它还声明了两个PropertyService
配置为
创建或替换PropertyService
bean 在各自的上下文中与 Mockito 模拟,
叫"parent"
和"child"
.因此,来自"parent"
上下文将
被注入propertyServiceInParent
字段,以及来自"child"
context 将被注入到propertyServiceInChild
田。
-
Java
-
Kotlin
@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
// ...
}