赞
踩
Spring TestContext
框架(位于org.springframework.test.context
包中)提供了通用的、注解驱动的单元和集成测试支持,这些支持与所使用的测试框架无关。TestContext
框架还非常重视约定优于配置,你可以通过基于注解的配置覆盖合理的默认值。
除了通用测试基础结构之外,TestContext
框架还为JUnit 4,JUnit Jupiter(AKA JUnit 5)和TestNG提供了显式支持。对于JUnit 4和TestNG,Spring提供了抽象支持类。此外,Spring为JUnit 4提供了自定义JUnit Runner和自定义JUnit规则,以及JUnit Jupiter的自定义扩展,可让你编写所谓的POJO测试类。不需要POJO测试类来扩展特定的类层次结构,例如抽象支持类。下一节概述了TestContext
框架的内部。如果你仅对使用框架感兴趣,而对使用自己的自定义监听器或自定义加载程序进行扩展不感兴趣,请直接转到配置(上下文管理 、依赖项注入、事务管理、支持类和注解支持部分。
该框架的核心由TestContextManager
类和TestContext
,TestExecutionListener
和SmartContextLoader
接口组成。为每个测试类创建一个TestContextManager
(例如,用于在JUnit Jupiter中的单个测试类中执行所有测试方法)。反过来,TestContextManager
管理包含当前测试上下文的TestContext
。随着测试的进行,TestContextManager
还更新了TestContext
的状态,并委托给TestExecutionListener
实现,该实现通过提供依赖项注入,管理事务等来检测实际的测试执行。SmartContextLoader
负责为给定的测试类加载ApplicationContext
。有关更多信息和各种实现的示例,请参见javadoc和Spring测试套件。
TestContext
TestContext
封装了在其中执行测试的上下文(与使用中的实际测试框架无关),并为其负责的测试实例提供了上下文管理和缓存支持。如果需要,TestContext
还委托给SmartContextLoader
来加载ApplicationContext
。
TestContextManager
TestContextManager
是Spring TestContext
框架的主要入口点,并负责管理单个TestContext
并在定义良好的测试执行点向每个注册的TestExecutionListener
发出事件信号:
before
类之前或在特定测试框架的所有方法之前。before
或在每个特定测试框架的方法之前。TestExecutionListener
TestExecutionListener
定义用于对由注册监听器的TestContextManager
发布的测试执行事件做出反应的API。请参阅TestExecutionListener配置。
上下文加载器
ContextLoader
是一个策略接口,用于为Spring TestContext
框架管理的集成测试加载ApplicationContext
。你应该实现SmartContextLoader
而不是此接口,以提供对组件类,激活的bean定义配置文件、测试属性源、上下文层次结构和WebApplicationContext
支持的支持。
SmartContextLoader
是ContextLoader
接口的扩展,它取代了原始的最小ContextLoader
SPI。具体来说,SmartContextLoader
可以选择处理资源位置、组件类或上下文初始化器。此外,SmartContextLoader
可以在其加载的上下文中设置激活Bean定义配置文件并测试属性源。
Spring提供了以下实现:
DelegatingSmartContextLoader
: 它是两个默认加载器之一,它在内部委派给AnnotationConfigContextLoader
、GenericXmlContextLoader
或GenericGroovyXmlContextLoader
,具体取决于为测试类声明的配置或默认位置或默认配置类的存在。仅当Groovy在类路径上时才启用Groovy支持。WebDelegatingSmartContextLoader
: 它是两个默认加载器之一,它在内部委派给AnnotationConfigWebContextLoader
、GenericXmlWebContextLoader或GenericGroovyXmlWebContextLoader
,具体取决于为测试类声明的配置或默认位置或默认配置类的存在。仅当测试类上存在@WebAppConfiguration
时,才使用Web ContextLoader
。仅当Groovy在类路径上时才启用Groovy支持。AnnotationConfigContextLoader
:从组件类加载标准ApplicationContext
。AnnotationConfigWebContextLoader
: 从组件类加载WebApplicationContext
。GenericGroovyXmlContextLoader
: 从Groovy脚本或XML配置文件的资源位置加载标准ApplicationContext
。GenericGroovyXmlWebContextLoader
: 从Groovy脚本或XML配置文件的资源位置加载WebApplicationContext
。GenericXmlContextLoader
: 从XML资源位置加载标准ApplicationContext
。GenericXmlWebContextLoader
: 从XML资源位置加载WebApplicationContext
。GenericPropertiesContextLoader
:从Java属性文件加载标准ApplicationContext
。Spring TestContext
框架内部的默认配置足以满足所有常见用例。但是,有时开发团队或第三方框架希望更改默认的ContextLoader
,实现自定义的TestContext
或ContextCache
,扩展默认的ContextCustomizerFactory
和TestExecutionListener
实现等等。为了对TestContext
框架的运行方式进行低级别控制,Spring提供了引导策略。
TestContextBootstrapper
定义了用于引导TestContext
框架的SPI。TestContextManager
使用TestContextBootstrapper
加载当前测试的TestExecutionListener
实现并构建它管理的TestContext
。你可以直接使用@BootstrapWith
或作为元注解,为测试类(或测试类层次结构)配置自定义引导策略。如果没有通过使用@BootstrapWith
显式配置引导程序,则根据@WebAppConfiguration
的存在,使用DefaultTestContextBootstrapper
或WebTestContextBootstrapper
。
由于TestContextBootstrapper
SPI将来可能会更改(以适应新的需求),我们强烈建议实现者不要直接实现此接口,而应扩展AbstractTestContextBootstrapper
或其具体子类之一。
TestExecutionListener
配置Spring提供了以下TestExecutionListener
实现,这些实现默认情况下按以下顺序注册:
ServletTestExecutionListener
:为WebApplicationContext
配置Servlet API模拟。DirtiesContextBeforeModesTestExecutionListener
:处理before
模式的@DirtiesContext
注解。DependencyInjectionTestExecutionListener
: 为测试实例提供依赖项注入。DirtiesContextTestExecutionListener
: 处理after
模式的@DirtiesContext
注解。TransactionalTestExecutionListener
: 提供具有默认回滚语义的事务测试执行。SqlScriptsTestExecutionListener
: 运行使用@Sql
注释配置的SQL脚本。EventPublishingTestExecutionListener
: 将测试执行事件发布到测试的ApplicationContext
中(请参阅测试执行事件)。注册TestExecutionListener
实现
你可以使用@TestExecutionListeners
注解为测试类及其子类注解TestExecutionListener
实现。有关详细信息和示例,请参见注解支持和@TestExecutionListeners的javadoc。
默认TestExecutionListener实现自动发现
通过使用@TestExecutionListeners
注册TestExecutionListener
实现适用于有限测试方案中使用的自定义监听器。但是,如果需要在整个测试套件中使用自定义监听器,则会变得很麻烦。通过SpringFactoriesLoader
机制支持自动发现默认的TestExecutionListener
实现,可以解决这个问题。
具体来说,spring-test
模块在其META-INF/spring.factories
属性文件中的key
为org.springframework.test.context.TestExecutionListener
下声明所有核心默认TestExecutionListener
实现。第三方框架和开发人员可以通过自己的META-INF/spring.factories
属性文件以相同的方式将自己的TestExecutionListener
实现贡献到默认监听器列表中。
TestExecutionListener顺序实现
当TestContext
框架通过上述SpringFactoriesLoader
机制发现默认TestExecutionListener
实现时,实例化的监听器将使用Spring的AnnotationAwareOrderComparator
进行排序,该类将使用Spring的Ordered
接口和@Order注解进行排序。Spring提供的AbstractTestExecutionListener
和所有默认的TestExecutionListener
实现以适当的值实现Ordered
。因此,第三方框架和开发人员应通过实施Ordered
或声明@Order
来确保按默认顺序注册其默认的TestExecutionListener
实现。请参阅javadoc以获取核心默认TestExecutionListener
实现的getOrder()
方法,以获取有关为每个核心监听器分配哪些值的详细信息。
TestExecutionListener合并实现
如果通过@TestExecutionListeners
注册了自定义TestExecutionListener
,则不会注册默认监听器。在大多数常见的测试方案中,这有效地迫使开发人员手动声明除任何自定义监听器之外的所有默认监听器。
下面的清单演示了这种配置样式:
@ContextConfiguration
@TestExecutionListeners({
MyCustomTestExecutionListener.class,
ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class
})
class MyTest {
// class body...
}
这种方法的挑战在于,它要求开发人员确切地知道默认情况下注册了哪些监听器。此外,默认的监听器集可以随版本的不同而变化-例如,在Spring框架4.1中引入了SqlScriptsTestExecutionListener
,在Spring框架4.2中引入了DirtiesContextBeforeModesTestExecutionListener
。此外,诸如Spring Boot和Spring Security之类的第三方框架通过使用上述自动发现机制注册了自己的默认TestExecutionListener
实现。
为避免必须了解并重新声明所有默认监听器,可以将@TestExecutionListeners
的mergeMode
属性设置为MergeMode.MERGE_WITH_DEFAULTS
。MERGE_WITH_DEFAULTS
表示应将本地声明的监听器与默认监听器合并。合并算法可确保从列表中删除重复项,并确保根据AnnotationAwareOrderComparator
的语义对合并后的监听器集进行排序,如Ordering TestExecutionListener实现中所述。如果监听器实现Ordered
或使用@Order
进行注解,则它可以影响将其与默认值合并的位置。否则,合并时,本地声明的监听器将追加到默认侦听器列表中。
例如,如果上一个示例中的MyCustomTestExecutionListener
类将顺序值(例如500)配置为小于ServletTestExecutionListener
的顺序(恰好是1000),则MyCustomTestExecutionListener
可以自动与默认列表合并。在ServletTestExecutionListener
前面,并且前面的示例可以替换为以下示例:
@ContextConfiguration
@TestExecutionListeners(
listeners = MyCustomTestExecutionListener.class,
mergeMode = MERGE_WITH_DEFAULTS
)
class MyTest {
// class body...
}
Spring框架5.2中引入的EventPublishingTestExecutionListener
提供了一种实现自定义TestExecutionListener
的替代方法。测试的ApplicationContext
中的组件可以监听EventPublishingTestExecutionListener
发布的以下事件,每个事件都与TestExecutionListener
API中的方法相对应。
BeforeTestClassEvent
PrepareTestInstanceEvent
BeforeTestMethodEvent
BeforeTestExecutionEvent
AfterTestExecutionEvent
AfterTestMethodEvent
AfterTestClassEvent
只有当
ApplicationContext
已经加载时,才会发布这些事件。
这些事件可能由于各种原因被使用,例如重置模拟bean或跟踪测试执行。使用测试执行事件而不是实现自定义TestExecutionListener
的一个优势是,测试执行事件可以由在测试ApplicationContext
中注册的任何Spring bean所使用,并且此类bean可以直接受益于依赖项注入和ApplicationContext
的其他功能。相反,TestExecutionListener
在ApplicationContext
中不是bean。
为了监听测试执行事件,Spring Bean可以选择实现org.springframework.context.ApplicationListener
接口。或者,可以使用@EventListener
注解监听器方法,并将监听方法配置为监听上面列出的特定事件类型之一(请参阅基于注解的事件监听器)。由于这种方法的流行,Spring提供了以下专用的@EventListener
注解,以简化测试执行事件监听器的注册。这些注解驻留在org.springframework.test.context.event.annotation
包中。
@BeforeTestClass
@PrepareTestInstance
@BeforeTestMethod
@BeforeTestExecution
@AfterTestExecution
@AfterTestMethod
@AfterTestClass
参考代码:
org.liyong.test.annotation.test.spring.TestExecutionEventTest
异常处理
默认情况下,如果测试执行事件监听器在使用事件时抛出异常,则该异常将传播到使用中的基础测试框架(例如JUnit或TestNG)。例如,如果使用BeforeTestMethodEvent
导致异常,则相应的测试方法将因异常而失败。相反,如果异步测试执行事件监听器引发异常,则该异常不会传播到基础测试框架。有关异步异常处理的更多详细信息,请查阅@EventListener
类级javadoc。
异步监听器
如果你希望特定的测试执行事件监听器异步处理事件,你可以使用Spring的常规@Async支持。有关更多详细信息,请查阅@EventListener
的类级javadoc。
参考代码:
org.liyong.test.annotation.test.spring.TestExecutionEventTest
每个TestContext
为其负责的测试实例提供上下文管理和缓存支持。测试实例不会自动接收对配置的ApplicationContext
的访问。但是,如果测试类实现ApplicationContextAware
接口,则将对ApplicationContext
的引用提供给测试实例。请注意,AbstractJUnit4SpringContextTests
和AbstractTestNGSpringContextTests
实现了ApplicationContextAware
,因此可以自动提供对ApplicationContext
的访问。
@Autowired
ApplicationContext
作为实现
ApplicationContextAware
接口的替代方法,你可以通过字段或setter
方法上的@Autowired
注解为测试类注入应用程序上下文,如以下示例所示:@SpringJUnitConfig class MyTest { @Autowired //1 ApplicationContext applicationContext; // class body... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 注入
ApplicationContext
。同样,如果将测试配置为加载
WebApplicationContext
,则可以将Web应用程序上下文注入到测试中,如下所示:@SpringJUnitWebConfig //1 class MyWebAppTest { @Autowired //2 WebApplicationContext wac; // class body... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 配置
WebApplicationContext
- 注入
WebApplicationContext
使用
@Autowired
的依赖关系注入是DependencyInjectionTestExecutionListener
提供的,它是默认配置的(参见测试装置的依赖注入)。
使用TestContext
框架的测试类不需要扩展任何特定的类或实现特定的接口来配置其应用程序上下文。而是通过在类级别声明@ContextConfiguration
注解来实现配置。如果你的测试类未明确声明应用程序上下文资源位置或组件类,则配置的ContextLoader
将确定如何从默认位置或默认配置类加载上下文。除了上下文资源位置和组件类之外,还可以通过应用程序上下文初始化程序配置应用程序上下文。
以下各节说明如何使用Spring的@ContextConfiguration
注解通过XML配置文件、Groovy
脚本、组件类(通常为@Configuration
类)或上下文初始化器来配置测试ApplicationContext
。另外,你可以为高级用例实现和配置自己的自定义SmartContextLoader
。
WebApplicationContext
通过XML资源配置上下文
若要使用XML配置文件为测试加载ApplicationContext
,请使用@ContextConfiguration
注解测试类,并使用包含XML配置元数据的资源位置的数组配置locations
属性。简单路径或相对路径(例如context.xml
)被视为相对于定义测试类的程序包的类路径资源。以斜杠开头的路径被视为绝对类路径位置(例如:/org/example/config.xml
)。照原样使用表示资源URL的路径(即以classpath:
、file:
、http:
等开头的路径)。
@ExtendWith(SpringExtension.class)
// ApplicationContext从根路径加载"/app-config.xml" 和
// "/test-config.xml"
@ContextConfiguration(locations={"/app-config.xml", "/test-config.xml"}) //1
class MyTest {
// class body...
}
locations
属性设置为XML文件列表。@ContextConfiguration
通过标准Java值属性为locations
属性支持别名。因此,如果不需要在@ContextConfiguration
中声明其他属性,你可以使用以下示例中演示的格式,省略locations
属性名称的声明并声明资源位置。
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-config.xml"}) //1
class MyTest {
// class body...
}
location
属性指定XML文件。如果你从@ContextConfiguration
注解中省略了位置和值属性,则TestContext
框架将尝试检测默认的XML资源位置。具体而言,GenericXmlContextLoader
和GenericXmlWebContextLoader
根据测试类的名称检测默认位置。如果你的类名为com.example.MyTest
则GenericXmlContextLoader
将classpath:com/example/MyTest-context.xml
加载应用程序上下文。以下示例显示了如何执行此操作:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTest-context.xml"
@ContextConfiguration //1
class MyTest {
// class body...
}
通过Groovy脚本配置上下文
要通过使用Groovy Bean定义DSL的Groovy脚本为测试加载ApplicationContext
,可以使用@ContextConfiguration
注解测试类,并使用包含Groovy脚本资源位置的数组配置location
或value
属性。Groovy脚本的资源查找语义与针对XML配置文件描述的语义相同。
激活Groovy脚本支持
如果类路径中有Groovy,那么就会自动启用使用Groovy脚本在Spring
TestContext
框架中加载ApplicationContext
的支持。
下面的示例显示如何指定Groovy配置文件:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "/AppConfig.groovy" and
// "/TestConfig.groovy" in the root of the classpath
@ContextConfiguration({"/AppConfig.groovy", "/TestConfig.Groovy"})
class MyTest {
// class body...
}
如果你从@ContextConfiguration
注解中省略了location
和value
属性,则TestContext
框架将尝试检测默认的Groovy脚本。具体来说,GenericGroovyXmlContextLoader
和GenericGroovyXmlWebContextLoader
根据测试类的名称检测默认位置。如果你的类名为com.example.MyTest
则Groovy上下文加载器将从classpath:com/example/MyTestContext.groovy
加载应用程序上下文。下面的示例演示如何使用默认值:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTestContext.groovy"
@ContextConfiguration //1
class MyTest {
// class body...
}
同时声明XML配置和Groovy脚本
你可以使用
@ContextConfiguration
的location
或value
属性同时声明XML配置文件和Groovy脚本。如果到配置的资源位置的路径以.xml
结尾,则使用XmlBeanDefinitionReader
加载该路径。否则,将使用GroovyBeanDefinitionReader
加载它。以下清单显示了如何在集成测试中将两者结合起来:
@ExtendWith(SpringExtension.class) // ApplicationContext将从 // "/app-config.xml" 和 "/TestConfig.groovy"加载上下文 @ContextConfiguration({ "/app-config.xml", "/TestConfig.groovy" }) class MyTest { // class body... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
通过组件类配置上下文
要使用组件类(请参见基于Java的容器配置)为测试加载ApplicationContext
,可以使用@ContextConfiguration
注解测试类,并使用包含对组件类的引用的数组来配置classes
属性。以下示例显示了如何执行此操作:
@ExtendWith(SpringExtension.class)
// ApplicationContext将从AppConfig和TestConfig加载上下文
@ContextConfiguration(classes = {AppConfig.class, TestConfig.class}) //1
class MyTest {
// class body...
}
组件类
术语
组件类
可以指以下任何一种:
- 用
@Configuration
注解的类。- 组件(即,用
@Component
、@Service
、@Repository
或其他构造型注解注释的类)。- 与JSR-330兼容的类,该类使用
javax.inject
注解进行了注释。- 包含
@Bean
方法的任何类。- 打算注册为Spring组件的任何其他类(即
ApplicationContext
中的Spring bean),可能利用单个自动构造函数的自动装配而无需使用Spring注解。有关组件类的配置和语义的更多信息,请参见@Configuration和@Bean的javadoc,尤其要注意
@Bean
Lite模式的讨论。
如果你从@ContextConfiguration
注解中省略了classes
属性,则TestContext
框架将尝试检测默认配置类的存在。具体来说,AnnotationConfigContextLoader
和AnnotationConfigWebContextLoader
检测到满足配置类实现要求的测试类的所有静态嵌套类,如@Configuration javadoc中所指定。请注意,配置类的名称是任意的。另外,如果需要,一个测试类可以包含多个静态嵌套配制类。在以下示例中,OrderServiceTest
类声明一个名为Config
的静态嵌套配置类,该配置类将自动用于为测试类加载ApplicationContext
:
@SpringJUnitConfig //1 // ApplicationContext将从内部潜逃静态累加载 class OrderServiceTest { @Configuration static class Config { // this bean will be injected into the OrderServiceTest class @Bean OrderService orderService() { OrderService orderService = new OrderServiceImpl(); // set properties, etc. return orderService; } } @Autowired OrderService orderService; @Test void testOrderService() { // test the orderService } }
XML、Groovy脚本、组件类混合
有时可能需要混合使用XML配置文件、Groovy脚本和组件类(通常为@Configuration
类)来为测试配置ApplicationContext
。如果在生产中使用XML配置,则可以决定要使用@Configuration
类为测试配置特定的Spring托管组件,反之亦然。
此外,某些第三方框架(例如Spring Boot)提供了一流的支持,可以同时从不同类型的资源(例如XML配置文件、Groovy脚本和@Configuration
类)中加载ApplicationContext
。过去,Spring框架不支持此标准部署。因此,Spring框架在spring-test
模块中提供的大多数SmartContextLoader
实现对于每个测试上下文仅支持一种资源类型。但是,这并不意味着你不能同时使用两者。通用规则的一个例外是GenericGroovyXmlContextLoader
和GenericGroovyXmlWebContextLoader
同时支持XML配置文件和Groovy脚本。此外,第三方框架可以选择通过@ContextConfiguration
支持位置和类的声明,并且,借助TestContext
框架中的标准测试支持,你可以选择以下选项。
如果要使用资源位置(例如XML或Groovy)和@Configuration
类的配置测试,则必须选择一个作为入口点,并且其中一个必须包含或导入另一个。例如,在XML或Groovy脚本中,可以通过使用组件扫描或将它们定义为普通的Spring bean来包括@Configuration
类,而在@Configuration
类中,可以使用@ImportResource
导入XML配置文件或Groovy脚本。请注意,此行为在语义上等同于你在生产环境中配置应用程序的方式:在生产配置中,你定义了一组XML或Groovy资源位置或一组@Configuration
类,从中加载了生产ApplicationContext
,但是你仍然包含或导入其他类型的配置的自由。
通过上下文初始化器配置上下文
若要使用上下文初始化程序为你的测试配置ApplicationContext
,请使用@ContextConfiguration
注解测试类,并使用包含对实现ApplicationContextInitializer
的类的引用的数组配置初始化程序属性。然后,使用声明的上下文初始值设定项来初始化为测试加载的ConfigurableApplicationContext
。请注意,每个声明的初始化程序支持的具体ConfigurableApplicationContext
类型必须与使用中的SmartContextLoader
创建的ApplicationContext
类型(通常是GenericApplicationContext
)兼容。此外,初始化程序的调用顺序取决于它们是实现Spring的Ordered
接口还是以Spring的@Order
注解或标准的@Priority
注解进行注释。下面的示例演示如何使用初始化程序:
@ExtendWith(SpringExtension.class)
// ApplicationContext将从TestConfig
// 和 通过TestAppCtxInitializer初始化
@ContextConfiguration(
classes = TestConfig.class,
initializers = TestAppCtxInitializer.class) //1
class MyTest {
// class body...
}
你还可以完全省略@ContextConfiguration
中的XML配置文件、Groovy脚本或组件类的声明,而仅声明ApplicationContextInitializer
类,然后这些类负责在上下文中注册Bean(例如,通过编程方式从XML文件加载Bean定义)或配置类。以下示例显示了如何执行此操作:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be initialized by EntireAppInitializer
// which presumably registers beans in the context
@ContextConfiguration(initializers = EntireAppInitializer.class) //1
class MyTest {
// class body...
}
参考代码:
org.liyong.test.annotation.test.spring.ContextInitializerTests
上下文配置继承
@ContextConfiguration
支持boolean
inheritLocations
和inheritinitialalizer
属性,它们表示是否应该继承由超类声明的资源位置或组件类和上下文初始化器。这两个标志的默认值为true
。这意味着测试类将继承资源位置或组件类以及任何超类申明的上下文初始化器。具体来说,将测试类的资源位置或组件类附加到由超类申明的资源位置或带注解的类的列表中。同样,将给定测试类的初始化程序添加到由测试超类定义的初始化程序集。因此,子类可以选择扩展资源位置、组件类或上下文初始化程序。
如果@ContextConfiguration
中的inheritLocations
或inheritInitializers
属性被设置为false
,则测试类的资源位置或组件类和上下文初始化器将分别有效地替代超类定义的配置。
在下一个使用XML资源位置的示例中,从Base-config.xml
和Extended-config.xml依
次加载ExtendedContext
的ApplicationContext
。因此,extended-config.xml
中定义的Bean可以覆盖(即替换)base-config.xml
中定义的那些。以下示例显示了一个类如何扩展另一个类并使用其自己的配置文件和超类的配置文件:
@ExtendWith(SpringExtension.class)
// ApplicationContext将从类路径根目录加载"/base-config.xml"
@ContextConfiguration("/base-config.xml") //1
class BaseTest {
// class body...
}
// ApplicationContext将从类路径根目录加载"/base-config.xml" 和
// "/extended-config.xml"
@ContextConfiguration("/extended-config.xml") //2
class ExtendedTest extends BaseTest {
// class body...
}
同样,在下一个使用组件类的示例中,从BaseConfig
和ExtendedConfig
类按该顺序加载ExtendedTest
的ApplicationContext
。因此,在ExtendedConfig
中定义的Bean可以覆盖(即替换)在BaseConfig
中定义的Bean。下面的示例显示一个类如何扩展另一个类,并同时使用自己的配置类和超类的配置类:
// ApplicationContext从BaseConfig加载
@SpringJUnitConfig(BaseConfig.class) //1
class BaseTest {
// class body...
}
// ApplicationContext将从BaseConfig和ExtendedConfig加载
@SpringJUnitConfig(ExtendedConfig.class) //2
class ExtendedTest extends BaseTest {
// class body...
}
在使用上下文初始化程序的下一个示例中,通过使用BaseInitializer
和ExtendedInitializer
初始化ExtendedTest
的ApplicationContext
。但是请注意,初始化程序的调用顺序取决于它们是实现Spring的Ordered
接口还是以Spring的@Order
注解或标准的@Priority
注解进行注释。以下示例显示了一个类如何扩展另一个类并使用其自己的初始化程序和超类的初始化程序:
// ApplicationContext将通过BaseInitializer初始化
@SpringJUnitConfig(initializers = BaseInitializer.class) //1
class BaseTest {
// class body...
}
// ApplicationContext将通过BaseInitializer
// 和 ExtendedInitializer初始化
@SpringJUnitConfig(initializers = ExtendedInitializer.class) //2
class ExtendedTest extends BaseTest {
// class body...
}
使用环境配置文件进行上下文配置
Spring框架对环境和配置文件(又名“bean定义配置文件”)的概念提供了一流的支持,可以配置集成测试来激活针对各种测试场景的特定bean定义配置文件。这可以通过使用@ActiveProfiles
注解测试类并提供在加载测试的ApplicationContext
时应激活的配置文件列表来实现。
你可以将
@ActiveProfiles
与SmartContextLoader
SPI的任何实现一起使用,但较早的ContextLoader
SPI的实现不支持@ActiveProfiles
。
考虑两个带有XML配置和@Configuration
类的示例:
<!-- app-config.xml --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="..."> <bean id="transferService" class="com.bank.service.internal.DefaultTransferService"> <constructor-arg ref="accountRepository"/> <constructor-arg ref="feePolicy"/> </bean> <bean id="accountRepository" class="com.bank.repository.internal.JdbcAccountRepository"> <constructor-arg ref="dataSource"/> </bean> <bean id="feePolicy" class="com.bank.service.internal.ZeroFeePolicy"/> <beans profile="dev"> <jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/> <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/> </jdbc:embedded-database> </beans> <beans profile="production"> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> </beans> <beans profile="default"> <jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/> </jdbc:embedded-database> </beans> </beans>
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "classpath:/app-config.xml"
@ContextConfiguration("/app-config.xml")
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// test the transferService
}
}
运行TransferServiceTest
时,会从类路径根目录中的app-config.xml
配置文件中加载其ApplicationContext
。如果检查app-config.xml
,可以看到accountRepository bean对dataSource
bean有依赖性。但是,dataSource
未被定义为顶级bean。相反,dataSource
定义了三次:在生产配置文件中、在开发配置文件中以及在默认配置文件中。
通过使用@ActiveProfiles(“dev”)
注解TransferServiceTest
,我们指示Spring TestContext
框架加载具有设置为{“dev”}
的激活配置文件的ApplicationContext
。结果,创建了一个嵌入式数据库,并用测试数据填充了数据库,并用对开发DataSource
的引用来连接accountRepository
bean。这可能是我们在集成测试中想要的。
有时将bean分配给默认配置文件很有用。只有在没有特别激活其他配置文件时,才会包含缺省配置文件中的bean。你可以使用它来定义在应用程序的默认状态中使用的后备bean。例如,你可以显式提供dev
和production
的数据源,但是当两者都不处于活动状态时,将内存中数据源定义为默认值。
以下代码清单演示了如何使用@Configuration
类而不是XML实现相同的配置和集成测试:
@Configuration
@Profile("dev")
public class StandaloneDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
@Configuration
@Profile("production")
public class JndiDataConfig {
@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
@Configuration
@Profile("default")
public class DefaultDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build();
}
}
@Configuration public class TransferServiceConfig { @Autowired DataSource dataSource; @Bean public TransferService transferService() { return new DefaultTransferService(accountRepository(), feePolicy()); } @Bean public AccountRepository accountRepository() { return new JdbcAccountRepository(dataSource); } @Bean public FeePolicy feePolicy() { return new ZeroFeePolicy(); } }
@SpringJUnitConfig({ TransferServiceConfig.class, StandaloneDataConfig.class, JndiDataConfig.class, DefaultDataConfig.class}) @ActiveProfiles("dev") class TransferServiceTest { @Autowired TransferService transferService; @Test void testTransferService() { // test the transferService } }
在此变体中,我们将XML配置分为四个独立的@Configuration
类:
ransferServiceConfig
: 通过使用@Autowired
进行依赖项注入来获取数据源。StandaloneDataConfig
: 为适合开发人员测试的嵌入式数据库定义数据源。JndiDataConfig
: 定义在生产环境中从JNDI检索的数据源。DefaultDataConfig
: 如果没有配置文件处于激活状态,则为默认的嵌入式数据库定义一个数据源。与基于XML的配置示例一样,我们仍然使用@ActiveProfiles("dev")
注解TransferServiceTest
,但是这次我们使用@ContextConfiguration
注解指定所有四个配置类。测试类的主体本身保持完全不变。
在一个给定项目中,跨多个测试类使用一组配置文件是很常见的。因此,为避免@ActiveProfiles
注解的重复声明,可以在基类中声明一次@ActiveProfiles
,子类会自动从基类继承@ActiveProfiles
配置。在以下示例中,@ActiveProfiles
的声明(以及其他注解)已移至抽象超类AbstractIntegrationTest
:
@SpringJUnitConfig({
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
// "dev"配置集成父类
class TransferServiceTest extends AbstractIntegrationTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// test the transferService
}
}
@ActiveProfiles
还支持可用于禁用激活配置文件的继承的InheritedProfiles
属性,如以下示例所示:
// "dev"配置被"production"覆盖
@ActiveProfiles(profiles = "production", inheritProfiles = false)
class ProductionTransferServiceTest extends AbstractIntegrationTest {
// test body
}
此外,有时有必要以编程方式而不是声明方式来解析测试的激活配置文件,例如,基于:
要以编程方式解析活动bean定义配置文件,可以实现自定义ActiveProfilesResolver
并使用@ActiveProfiles
的resolver
属性对其进行注册。有关更多信息,请参见相应的javadoc。下面的示例演示如何实现和注册自定义的OperatingSystemActiveProfilesResolver
:
// "dev"配置通过自定义解析器编程式地覆盖
@ActiveProfiles(
resolver = OperatingSystemActiveProfilesResolver.class,
inheritProfiles = false)
class TransferServiceTest extends AbstractIntegrationTest {
// test body
}
public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver {
@Override
public String[] resolve(Class<?> testClass) {
String profile = ...;
// determine the value of profile based on the operating system
return new String[] {profile};
}
}
通过测试属性源配置上下文
Spring框架对具有属性源层次结构的环境概念提供了一流的支持,你可以使用特定于测试的属性源配置集成测试。与@Configuration
类上使用的@PropertySource
注解相反,可以在测试类上声明@TestPropertySource
注解,以声明测试属性文件或内联属性的资源位置。这些测试属性源被添加到环境中为带注解的集成测试加载的ApplicationContext
的PropertySources
集合中。
你可以将
@TestPropertySource
与SmartContextLoader
SPI的任何实现一起使用,但是较早的ContextLoader
SPI的实现不支持@TestPropertySource
。
SmartContextLoader
的实现可通过MergedContextConfiguration
中的getPropertySourceLocations()
和getPropertySourceProperties()
方法访问合并的测试属性源值。
声明测试属性源
你可以使用@TestPropertySource的location
或value
属性来配置测试属性文件。
支持传统属性文件格式和基于XML的属性文件格式,例如: classpath:/com/example/test.properties
或file:///path/to/file.xml
。
每个路径都被解析为Spring资源。普通路径(例如test.properties
)被视为相对于定义测试类的程序包的类路径资源。以斜杠开头的路径被视为绝对类路径资源(例如:/org/example/test.xml
)。通过使用指定的资源协议加载引用URL的路径(例如,以classpath:
、file:
或http:
为前缀的路径)。不允许使用资源位置通配符(例如* /.properties
)每个位置都必须精确评估为一个.properties
或.xml
资源。
以下示例使用测试属性文件:
@ContextConfiguration
@TestPropertySource("/test.properties") //1
class MyIntegrationTests {
// class body...
}
你可以使用@TestPropertySource
的properties
属性,以键/值对的形式配置内联属性,如下例所示。所有键值对都作为优先级最高的单个测试PropertySource
添加到封闭环境中。
键值对支持的语法与为Java属性文件中的条目定义的语法相同:
key=value
key:value
key value
下面的示例设置两个内联属性:
@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port: 4242"}) //1
class MyIntegrationTests {
// class body...
}
从Spring框架5.2开始,
@TestPropertySource
可以用作可重复注解。这意味着你可以在单个测试类上具有@TestPropertySource
的多个声明,其后的@TestPropertySource
注解中的locations
和properties
将覆盖先前的@TestPropertySource
注解中的locations
和properties
。此外,你可以在一个测试类上声明多个组合注解,每个注解都用
@TestPropertySource
进行元注解,所有这些@TestPropertySource
声明都将贡献给你的测试属性源。直接呈现的@TestPropertySource注解
总是优先于元呈现的@TestPropertySource
注解。换句话说,直接存在的@TestPropertySource
注解中的locations
和properties
将覆盖@TestPropertySource
注解中用作元注解的locations
和properties
。
默认属性文件检测
如果@TestPropertySource
被声明为空注解(即,没有locations
或properties
的显式值),则尝试检测相对于声明该注解的类的默认属性文件。例如,如果带注解的测试类是com.example.MyTest
,则相应的对认属性文件是classpath:com/example/MyTest.properties
。如果无法检测到默认值,则抛出IllegalStateException
。
优先顺序
测试属性的优先级高于在操作系统环境、Java系统属性或应用程序通过使用@PropertySource
声明性地或以编程方式添加的属性源中定义的属性。因此,测试属性可用于有选择地覆盖从系统和应用程序属性源加载的属性。此外,内联属性优先于从资源位置加载的属性。但是请注意,通过@DynamicPropertySource
注册的属性比通过@TestPropertySource
加载的属性具有更高的优先级。
在下一个示例中,timezone
和port
属性以及在/test.properties
中定义的任何属性都将覆盖在系统和应用程序属性源中定义的同名属性。此外,如果/test.properties
文件定义了timezone
和port
属性的条目,则这些条目将被使用properties
属性声明的内联属性所覆盖。
@ContextConfiguration
@TestPropertySource(
locations = "/test.properties",
properties = {"timezone = GMT", "port: 4242"}
)
class MyIntegrationTests {
// class body...
}
继承和覆盖测试属性源
@TestPropertySource
支持布尔型的inheritLocations
和inheritProperties
属性,它们表示属性文件的资源位置和超类声明的内联属性是否应该被继承。这两个标志的默认值为true
。这意味着测试类将继承任何超类声明的位置和内联属性。具体来说,测试类的位置和内联属性附加到父类声明的位置和内联属性。因此,子类可以选择扩展位置和内联属性。注意,后面出现的属性会隐藏(即覆盖)前面出现的同名属性。此外,前面提到的优先规则也适用于继承的测试属性源。
如果@TestPropertySource
中的InheritLocations
或InheritProperties
属性设置为false
,则分别为测试类设置位置或内联属性,并有效替换超类定义的配置。
在下一个示例中,BaseTest
的ApplicationContext
是通过只使用base
加载的。属性文件作为测试属性源。相反,ExtendedTest
的ApplicationContext
是通过使用base
加载的属性和扩展。属性文件作为测试属性源位置。下面的示例演示如何使用属性文件在子类及其超类中定义属性:
@TestPropertySource("base.properties")
@ContextConfiguration
class BaseTest {
// ...
}
@TestPropertySource("extended.properties")
@ContextConfiguration
class ExtendedTest extends BaseTest {
// ...
}
在下一个示例中,仅使用内联的key1
属性来加载BaseTest
的ApplicationContext
。相反,使用内联的key1
和key2
属性来加载ExtendedTest
的ApplicationContext
。下面的示例演示如何通过使用内联属性在子类及其父类中定义属性:
@TestPropertySource(properties = "key1 = value1")
@ContextConfiguration
class BaseTest {
// ...
}
@TestPropertySource(properties = "key2 = value2")
@ContextConfiguration
class ExtendedTest extends BaseTest {
// ...
}
通过动态属性源配置上下文
从Spring框架5.2.5开始,TestContext
框架通过@DynamicPropertySource
注解提供对动态属性的支持。此注解可用于需要向为集成测试加载的ApplicationContext
的环境中的PropertySources
集添加带有动态值的属性的集成测试。
@DynamicPropertySource
注解及其支持的基础结构最初旨在使基于Testcontainers
的测试中的属性易于暴露于Spring集成测试。但是,此功能还可以用于其生命周期在测试的ApplicationContext
之外维护的任何形式的外部资源。
与在类级别应用@TestPropertySource
注解相反,@DynamicPropertySource
必须应用于接受单个DynamicPropertyRegistry
参数的静态方法,该参数用于向环境添加名称/值
对。值是动态的,并通过Supplier
提供,只有在解析属性时才调用Supplier
。通常,方法引用被用来提供值,如下面的例子所示,它使用Testcontainers
项目在Spring ApplicationContext
之外管理一个Redis容器。通过redis.host
和redis.port
属性,测试的ApplicationContext
中的组件可以使用托管Redis容器的IP地址和端口。这些属性可以通过Spring的环境抽象访问,或者直接注入到Spring管理的组件中,例如分别通过@Value("${redis.host}")
和@Value("${redis.port}")
。
@SpringJUnitConfig(/* ... */) @Testcontainers class ExampleIntegrationTests { @Container static RedisContainer redis = new RedisContainer(); @DynamicPropertySource static void redisProperties(DynamicPropertyRegistry registry) { registry.add("redis.host", redis::getContainerIpAddress); registry.add("redis.port", redis::getMappedPort); } // tests ... }
优先顺序
动态属性的优先级高于@TestPropertySource
、操作系统的环境、Java系统属性或应用程序通过@PropertySource
声明性地或以编程方式添加的属性源中加载的属性。因此,动态属性可用于有选择地覆盖通过@TestPropertySource
、系统属性源和应用程序属性源加载的属性。
加载WebApplicationContext
若要指示TestContext
框架加载WebApplicationContext
而不是标准ApplicationContext
,可以使用@WebAppConfiguration
注解各自的测试类。
测试类上@WebAppConfiguration
的存在指示TestContext
框架(TCF)应该为集成测试加载WebApplicationContext
(WAC)。TCF在后台确保创建了MockServletContext
并将其提供给测试的WAC。默认情况下,你的MockServletContext
的基本资源路径设置为src/main/webapp
。这被解释为相对于JVM根目录的路径(通常是项目的路径)。如果你熟悉Maven项目中Web应用程序的目录结构,则知道src/main/webapp
是WAR根目录的默认位置。如果需要覆盖此默认值,则可以提供@WebAppConfiguration
注解的替换路径(例如,@WebAppConfiguration(“src/test/webapp”))
。如果你希望从类路径而不是文件系统中引用基本资源路径,则可以使用Spring的classpath:
前缀。
请注意,Spring对WebApplicationContext
实现的测试支持与其对标准ApplicationContext
实现的支持相当。使用WebApplicationContext
进行测试时,可以使用@ContextConfiguration
声明XML配置文件、Groovy脚本或@Configuration
类。你还可以自由地使用任何其他测试注解,如@ActiveProfiles
、@Testexecutionlistener
、@Sql
、@Rollback
和其他。
本节的其余示例展示了加载WebApplicationContext
的一些不同配置选项。以下示例显示了TestContext
框架对配置约定的支持:
@ExtendWith(SpringExtension.class)
// defaults to "file:src/main/webapp"
@WebAppConfiguration
// detects "WacTests-context.xml" in the same package
// or static nested @Configuration classes
@ContextConfiguration
class WacTests {
//...
}
如果使用@WebAppConfiguration
注解测试类而未指定资源基本路径,则资源路径实际上默认为file:src/main/webapp
。同样,如果在声明@ContextConfiguration
时未指定资源位置、组件类或上下文初始化程序,则Spring会尝试使用约定(也就是说,WacTests-context.xml
与WacTests
类或静态嵌套@Configuration
类位于同一包中)。
以下示例显示如何使用@WebAppConfiguration
显式声明资源基本路径和使用@ContextConfiguration
显式声明XML资源位置:
@ExtendWith(SpringExtension.class)
// file system resource
@WebAppConfiguration("webapp")
// classpath resource
@ContextConfiguration("/spring/test-servlet-config.xml")
class WacTests {
//...
}
这里要注意的重要一点是具有这两个注解的路径的语义不同。默认情况下,@ WebAppConfiguration
资源路径基于文件系统,而@ContextConfiguration
资源位置基于类路径。下面的示例显示,我们可以通过指定Spring资源前缀来覆盖两个注解的默认资源语义:
@ExtendWith(SpringExtension.class)
// classpath resource
@WebAppConfiguration("classpath:test-web-resources")
// file system resource
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml")
class WacTests {
//...
}
将本示例中的注解与上一个示例进行对比。
使用Web Mock工作
为了提供全面的Web测试支持,TestContext
框架具有默认启用的ServletTestExecutionListener
。在针对WebApplicationContext
进行测试时,此TestExecutionListener
会在每个测试方法之前使用Spring Web的RequestContextHolder
来设置默认的线程本地状态,并根据通过@WebAppConfiguration
配置的基本资源路径创建MockHttpServletRequest
、MockHttpServletResponse
和ServletWebRequest
。
ServletTestExecutionListener
还确保可以将MockHttpServletResponse
和ServletWebRequest
注入到测试实例中,并且一旦测试完成,它将清除线程本地状态。
一旦为测试加载了WebApplicationContext
,你可能会发现你需要与Web模拟进行交互,例如,在调用Web组件之后设置测试fixture
或执行断言。以下示例显示可以将哪些模拟自动装配到你的测试实例。请注意,WebApplicationContext
和MockServletContext
都缓存在测试套件中,而其他模拟则由ServletTestExecutionListener
针对每个测试方法进行管理。
@SpringJUnitWebConfig class WacTests { @Autowired WebApplicationContext wac; // cached @Autowired MockServletContext servletContext; // cached @Autowired MockHttpSession session; @Autowired MockHttpServletRequest request; @Autowired MockHttpServletResponse response; @Autowired ServletWebRequest webRequest; //... }
上下文缓存
一旦TestContext
框架为测试加载了ApplicationContext
(或WebApplicationContext
),该上下文将被缓存并重新用于在同一测试套件中声明相同唯一上下文配置的所有后续测试。要了解缓存的工作原理,重要的是要了解unique
和测试套件
的含义。
可以通过用于加载它的配置参数的组合来唯一标识ApplicationContext
。因此,使用配置参数的唯一组合来生成一个键,在该键下缓存上下文。TestContext
框架使用以下配置参数来构建上下文缓存键:
locations
(来自@ContextConfiguration
)classes
(来自@ContextConfiguration
)contextInitializerClasses
(来自@ContextConfiguration
)contextCustomizers
(来自ContextCustomizerFactory
)其中包括@DynamicPropertySource
方法,以及Spring Boot测试支持中的各种功能,例如@MockBean
和@SpyBean
。contextLoader
(来自@ContextConfiguration
)parent
(来自@ContextHierarchy
)activeProfiles
(来自@ActiveProfiles
)propertySourceLocations
(来自@TestPropertySource
)propertySourceProperties
(来自@TestPropertySource
)resourceBasePath
(来自@WebAppConfiguration
)例如,如果TestClassA
为@ContextConfiguration
的location
(或value
)属性指定{“app-config.xml”,“test-config.xml”}
,则TestContext
框架将加载相应的ApplicationContext
并将其存储在静态上下文缓存中仅基于那些位置的key下。因此,如果TestClassB
还为其位置(通过继承显式或隐式)定义了{“app-config.xml”,“test-config.xml”}
,但未定义@WebAppConfiguration
、不同的ContextLoader
、不同的激活配置文件、不同的上下文初始化程序、不同的测试属性源或不同的父上下文,则两个测试类将共享相同的ApplicationContext
。这意味着(每个测试套件)仅需加载一次加载应用程序上下文的设置成本,并且随后的测试执行要快得多。
测试套件和分支流程
Spring
TestContext
框架将应用程序上下文存储在静态缓存中。这意味着上下文实际上是存储在静态变量中的。换句话说,如果测试是在单独的进程中执行的,则在每个测试执行之间都会清除静态缓存,从而有效地禁用了缓存机制。为了从缓存机制中受益,所有测试必须在同一进程或测试套件中运行。这可以通过在IDE中以组的形式执行所有测试来实现。同样,在使用诸如Ant、Maven或Gradle之类的构建框架执行测试时,确保该构建框架不会在测试之间进行派生(fork多个进程)很重要。例如,如果将Maven Surefire插件的forkMode设置为always或pertest,则
TestContext
框架将无法在测试类之间缓存应用程序上下文,因此,构建过程的运行速度将大大降低。
上下文缓存的大小以默认的最大32为界。只要达到最大大小,就会使用最近最少使用(LRU)驱逐策略来驱逐和关闭旧的上下文。你可以通过设置名为spring.test.context.cache.maxSize
的JVM系统属性,从命令行或构建脚本中配置最大大小。或者,你可以使用SpringProperties
API以编程方式设置相同的属性。
由于在给定的测试套件中加载大量的应用程序上下文会导致该套件花费不必要的长时间来执行,因此准确地知道已加载和缓存了多少个上下文通常是有益的。要查看基础上下文缓存的统计信息,可以将org.springframework.test.context.cache
日志记录类别的日志级别设置为DEBUG
。
在不太可能的情况下,测试破坏了应用程序上下文并需要重新加载(例如,通过修改bean定义或应用程序对象的状态),你可以使用@DirtiesContext
注解测试类或测试方法(请参阅@DirtiesContext
中对@DirtiesContext的讨论)。这指示Spring在运行需要相同应用程序上下文的下一个测试之前,从缓存中删除上下文并重建应用程序上下文。请注意,默认情况下启用的DirtiesContextBeforeModesTestExecutionListener
和DirtiesContextTestExecutionListener
提供了对@DirtiesContext
注解的支持。
上下文层级
在编写依赖于已加载的Spring ApplicationContext
的集成测试时,通常足以针对单个上下文进行测试。但是,有时需要对ApplicationContext
实例的层次结构进行测试是有益的,甚至是必要的。例如,如果你正在开发Spring MVC Web应用程序,则通常由Spring的ContextLoaderListener
加载根WebApplicationContext
,由Spring的DispatcherServlet
加载子WebApplicationContext
。这将导致父子上下文层次结构,其中共享组件和基础设施配置在根上下文中声明,并由特定于web的组件在子上下文中使用。在Spring Batch应用程序中可以找到另一个用例,在该应用程序中,你通常具有一个父上下文,该上下文为共享批处理基础结构提供配置,而子上下文则为特定批处理作业的配置提供配置。
你可以通过在单个测试类上或在测试类层次结构中使用@ContextHierarchy
注解声明上下文配置来编写使用上下文层次结构的集成测试。如果在测试类层次结构中的多个类上声明了上下文层次结构,则还可以合并或覆盖上下文层次结构中特定命名级别的上下文配置。合并层次结构中给定级别的配置时,配置资源类型(即XML配置文件或组件类)必须一致。否则,在使用不同资源类型配置的上下文层次结构中具有不同级别是完全可以接受的。
本节中其余的基于JUnit Jupiter的示例显示了需要使用上下文层次结构的集成测试的常见配置方案。
具有上下文层次结构的单个测试类
ControllerIntegrationTests
通过声明一个上下文层次结构来代表Spring MVC Web应用程序的典型集成测试场景,该上下文层次结构包含两个级别,一个层次用于根WebApplicationContext
(通过使用TestAppConfig
@Configuration类加载),一个层次用于调度程序Servlet WebApplicationContext
(通过使用WebConfig
@Configuration
类加载)。自动装配到测试实例的WebApplicationContext
是用于子上下文(即,层次结构中的最低上下文)的WebApplicationContext
。
以下清单显示了此配置方案:
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = TestAppConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
class ControllerIntegrationTests {
@Autowired
WebApplicationContext wac;
// ...
}
参考代码:
org.liyong.test.annotation.test.spring.ControllerIntegrationTests
具有隐式父上下文的类层次结构
本示例中的测试类在测试类层次结构中定义了上下文层次结构。AbstractWebTests
在Spring驱动的Web应用程序中声明根WebApplicationContext
的配置。但是请注意,AbstractWebTests
不会声明@ContextHierarchy
。因此,AbstractWebTests
的子类可以选择参与上下文层次结构或遵循@ContextConfiguration
的标准语义。SoapWebServiceTests
和RestWebServiceTests
都扩展了AbstractWebTests
并使用@ContextHierarchy
定义了上下文层次结构。结果是,加载了三个应用程序上下文(每个@ContextConfiguration
声明一个),并且基于AbstractWebTests
中的配置加载的应用程序上下文被设置为具体子类加载的每个上下文的父上下文。
@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 {}
参考代码:
org.liyong.test.annotation.test.spring.RestWebServiceTests
合并上下文层次结构配置的类层次结构
此示例中的类显示了使用命名层次结构级别的目的,以及合并上下文层次结构中特定级别的配置。BaseTests
在层次结构中定义了两个级别,parent
和child
。ExtendedTests
扩展BaseTests
并指示Spring TestContext
框架合并子层次结构级别的上下文配置,方法是确保在@ContextConfiguration
的name属性中声明的名称均为子元素。结果是加载了三个应用程序上下文:一个用于/app-config.xml
、一个用于/user-config.xml
、一个用于{/user-config.xml
,/order-config.xml}
。与前面的示例一样,将从/app-config.xml
加载的应用程序上下文设置为从/user-config.xml
和{“/user-config.xml","/order-config.xml“}
加载的上下文的父上下文(合并配置文件)。以下清单显示了此配置方案:
@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 {}
参考代码:
org.liyong.test.annotation.test.spring.ExtendedTests
具有覆盖的上下文层次结构配置的类层次结构
与前面的示例相反,此示例演示了如何通过将@ContextConfiguration
中的InheritLocations
标志设置为false
来覆盖上下文层次结构中给定命名级别的配置。因此,ExtendedTests
的应用程序上下文仅从/test-user-config.xml
加载,并且其父级设置为从/app-config.xml
加载的上下文。以下清单显示了此配置方案:
@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 {}
清除上下文层次结构中的上下文
如果你在一个测试中使用@DirtiesContext,该测试的上下文被配置为上下文层次结构的一部分,那么你可以使用hierarchyMode标志来控制如何清除上下文缓存。有关更多详细信息,请参见Spring Testing Annotations中的@DirtiesContext和@DirtiesContext javadoc的讨论。
参考代码:
org.liyong.test.annotation.test.spring.ExtendedTests1
当使用DependencyInjectionTestExecutionListener
(默认配置)时,测试实例的依赖项是从使用@ContextConfiguration
或相关注解配置应用程序上下文中的bean注入的。你可以使用setter
注入、字段注入、或同时使用这两种方法,具体取决于你选择的注解以及是否将它们放在setter
方法或字段上。如果你使用的是JUnit Jupiter,则还可以选择使用构造函数注入(请参阅带有SpringExtension的依赖注入)。为了与Spring基于注解的注入支持保持一致,你还可以将Spring的@Autowired
注解或JSR-330中的@Inject
注解用于字段注入和setter 注入。
对于JUnit Jupiter以外的测试框架,
TestContext
框架不参与测试类的实例化。因此,将@Autowired
或@Inject
用于构造函数对测试类无效。
尽管在生产代码中不鼓励使用字段注入,但是在测试代码中字段注入实际上是很自然的。理由是你永远不会直接实例化测试类。因此,不需要在测试类上调用公共构造函数或
setter
方法。
因为@Autowired
用于按类型执行自动装配,所以如果你具有多个相同类型的Bean定义,那么对于那些特定的Bean,你将不能依靠这种方法。在这种情况下,可以将@Autowired
与@Qualifier
结合使用。你也可以选择将@Inject
与@Named
结合使用。或者,如果你的测试类可以访问其ApplicationContext
,则可以通过使用(例如)对applicationContext.getBean(“ titleRepository“,TitleRepository.class)
的调用来执行显式查找。
如果你不希望将依赖项注入应用于测试实例,请不要使用@Autowired
或@Inject
注解字段或设置器方法。或者,你可以通过显式地用@TestExecutionListeners
配置你的类,并从监听器列表中忽略DependencyInjectionTestExecutionListener.class
来禁用依赖注入。
考虑测试HibernateTitleRepository
类的场景,如目标部分所述。接下来的两个代码清单演示了@Autowired
在字段和setter
方法上的用法。在所有示例代码清单之后显示了应用程序上下文配置。
以下代码清单中的依赖项注入行为并非特定于JUnit Jupiter。相同的
DI
技术可以与任何受支持的测试框架结合使用。以下示例对静态断言方法(例如
assertNotNull()
)进行了调用,但没有在声明前添加Assertions
。在这种情况下,假定该方法是通过示例中未显示的import static
声明正确导入的。
第一个代码清单显示了使用@Autowired
进行字段注入的测试类的基于JUnit Jupiter的实现:
@ExtendWith(SpringExtension.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {
// this instance will be dependency injected by type
@Autowired
HibernateTitleRepository titleRepository;
@Test
void findById() {
Title title = titleRepository.findById(new Long(10));
assertNotNull(title);
}
}
或者,你可以将类配置为使用@Autowired
进行setter
注入,如下所示:
@ExtendWith(SpringExtension.class) // specifies the Spring configuration to load for this test fixture @ContextConfiguration("repository-config.xml") class HibernateTitleRepositoryTests { // this instance will be dependency injected by type HibernateTitleRepository titleRepository; @Autowired void setTitleRepository(HibernateTitleRepository titleRepository) { this.titleRepository = titleRepository; } @Test void findById() { Title title = titleRepository.findById(new Long(10)); assertNotNull(title); } }
前面的代码清单使用@ContextConfiguration
注解引用的相同XML上下文文件(即,repository-config.xml
)。下面显示了此配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- this bean will be injected into the HibernateTitleRepositoryTests class --> <bean id="titleRepository" class="com.foo.repository.hibernate.HibernateTitleRepository"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <!-- configuration elided for brevity --> </bean> </beans>
如果你是从Spring提供的测试基类扩展而来的,而该基类恰巧在其
setter
方法之一上使用@Autowired
,则可能在应用程序上下文中定义了多个受影响类型的Bean(例如,多个DataSource
Bean)。在这种情况下,你可以覆盖setter方法,并使用@Qualifier
注解指示特定的目标bean,如下所示(但请确保也委托给超类中的重写方法):// ... @Autowired @Override public void setDataSource(@Qualifier("myDataSource") DataSource dataSource) { super.setDataSource(dataSource); } // ...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
指定的限定符值指示要注入的特定
DataSource
Bean,从而将类型匹配的范围缩小到特定Bean。其值与相应的定义中的声明匹配。Bean名称用作后备限定符值,因此你也可以在该名称中有效地指向特定的Bean(如先前所示,假设myDataSource
是BeanID
)。
Spring从早期开始就支持Request
范围和Session
范围的Bean,你可以按照以下步骤测试Request
范围和Session
范围的Bean:
@WebAppConfiguration
注解测试类,确保为测试加载WebApplicationContext
。Request
或Session
注入到测试实例中,并根据需要准备测试装置。WebApplicationContext
中检索到的Web组件(具有依赖项注入)。下一个代码片段显示了登录用例的XML配置。注意,userService
bean与请求范围的loginAction
bean有依赖关系。另外,通过使用SpEL
表达式实例化LoginAction
,该表达式从当前HTTP请求中检索用户名和密码。在我们的测试中,我们想通过TestContext
框架管理的模拟来配置这些请求参数。以下清单显示了此用例的配置:
Request作用域bean配置
<beans>
<bean id="userService" class="com.example.SimpleUserService"
c:loginAction-ref="loginAction"/>
<bean id="loginAction" class="com.example.LoginAction"
c:username="#{request.getParameter('user')}"
c:password="#{request.getParameter('pswd')}"
scope="request">
<aop:scoped-proxy/>
</bean>
</beans>
在RequestScopedBeanTests
中,我们将UserService
(即被测对象)和MockHttpServletRequest
都注入到我们的测试实例中。在requestScope()
测试方法中,我们通过在提供的MockHttpServletRequest
中设置请求参数来设置测试配置。当在我们的userService
上调用loginUser()
方法时,可以确保该用户服务可以访问当前MockHttpServletRequest
(即我们刚刚设置参数的那个)在请求范围内的loginAction
。然后,我们可以根据用户名和密码的已知输入对结果进行断言。以下清单显示了如何执行此操作:
@SpringJUnitWebConfig
class RequestScopedBeanTests {
@Autowired UserService userService;
@Autowired MockHttpServletRequest request;
@Test
void requestScope() {
request.setParameter("user", "enigma");
request.setParameter("pswd", "$pr!ng");
LoginResults results = userService.loginUser();
// assert results
}
}
以下代码段类似于我们之前针对请求范围的Bean看到的代码段。但是,这一次,userService
bean与会话范围的userPreferences
bean有依赖关系。注意,通过使用SpEL表达式实例化UserPreferences
bean,该SpEL表达式从当前HTTP会话中检索主题。在我们的测试中,我们需要在由TestContext
框架管理的模拟会话中配置主题。以下示例显示了如何执行此操作:
Session作用域bean配置
<beans>
<bean id="userService" class="com.example.SimpleUserService"
c:userPreferences-ref="userPreferences" />
<bean id="userPreferences" class="com.example.UserPreferences"
c:theme="#{session.getAttribute('theme')}"
scope="session">
<aop:scoped-proxy/>
</bean>
</beans>
在SessionScopedBeanTests
中,我们将UserService
和MockHttpSession
注入我们的测试实例。在sessionScope()
测试方法中,我们通过在提供的MockHttpSession
中设置期望的主题属性来设置测试配置。当在我们的userService
上调用processUserPreferences()
方法时,我们可以确保用户服务可以访问当前MockHttpSession
的会话范围的userPreferences
,并且可以基于配置的主题对结果执行断言。以下示例显示了如何执行此操作:
@SpringJUnitWebConfig
class SessionScopedBeanTests {
@Autowired UserService userService;
@Autowired MockHttpSession session;
@Test
void sessionScope() throws Exception {
session.setAttribute("theme", "blue");
Results results = userService.processUserPreferences();
// assert results
}
}
参考代码:
org.liyong.test.annotation.test.spring.SessionScopedBeanTests
在TestContext
框架中,事务由TransactionalTestExecutionListener
进行管理,默认情况下配置该事务,即使你没有在测试类上显示声明@TestExecutionListeners
也不例外。但是,要启用对事务的支持,必须在ApplicationContext
中配置使用@ContextConfiguration
语义加载的PlatformTransactionManager
bean(稍后将提供更多详细信息)。此外,你必须在测试的类或方法级别声明Spring的@Transactional
注解。
测试事物管理
测试管理的事务是通过使用TransactionalTestExecutionListener
声明式管理的事务,或者是通过使用TestTransaction
以编程方式管理的事务(稍后描述)。你不应将此类事务与Spring托管的事务(由Spring在加载的Test中直接管理的事务)或应用程序托管的事务(在测试调用的应用程序代码中以编程方式管理的事务)混淆。Spring管理的事务和应用程序管理的事务通常参与测试管理的事务。但是,如果Spring管理的事务或应用程序管理的事务配置除REQUIRED
或SUPPORTS
之外的任何传播类型,则应谨慎使用(有关详细信息,请参见关于事务传播的讨论)。
抢占式超时和测试管理事务
当在测试框架中使用任何形式的抢占式超时并结合使用Spring的测试管理事务时,必须谨慎。
具体来说,Spring的测试支持在调用当前测试方法之前将事务状态绑定到当前线程(通过
java.lang.ThreadLocal
变量)。如果测试框架在新线程中调用当前测试方法以支持抢占式超时,则在当前测试方法内执行的任何操作都不会在测试管理的事务内调用。因此,任何此类操作的结果都不会随测试管理的事务回滚。相反,即使Spring适当地回滚了测试管理的事务,此类操作仍将提交给持久性存储(例如关系数据库)。可能发生这种情况的情况包括但不限于以下情况。
- JUnit 4的
@Test(timeout =…)
支持和TimeOut
规则org.junit.jupiter.api.Assertions
类中的JUnit Jupiter的assertTimeoutPreemptively(…)
方法- TestNG的
@Test(timeOut =…)
支持
激活和禁止事物
默认情况下,使用@Transactional
注解测试方法会导致测试在事务中运行,该事务在测试完成后会自动回滚。如果用@Transactional
注解测试类,则该类层次结构中的每个测试方法都在事务中运行。未用@Transactional
注解的测试方法(在类或方法级别)不在事务内运行。请注意,测试生命周期方法不支持@Transactional
,例如,使用JUnit Jupiter的@BeforeAll
,@BeforeEach
等进行注解的方法。此外,用@Transactional
注解但传播属性设置为NOT_SUPPORTED
的测试不在事务内运行。
@Transactional
支持属性表格:
属性 | 测试管理事务的支持 |
---|---|
value 和 transactionManager | yes |
propagation | 仅仅支持Propagation.NOT_SUPPORTED |
isolation | no |
timeout | no |
readOnly | no |
rollbackFor 和 rollbackForClassName | no : 使用TestTransaction.flagForRollback() 替换 |
noRollbackFor 和 noRollbackForClassName | no : 使用TestTransaction.flagForCommit() 替换 |
方法级生命周期方法(例如,用JUnit Jupiter的
@BeforeEach
或@AfterEach
注解的方法)在测试管理的事务中运行。另一方面,套件级和类级生命周期方法(例如,以JUnit Jupiter的@BeforeAll
或@AfterAll
注解的方法以及以TestNG的@BeforeSuite
、@AfterSuite
、@BeforeClass
或@AfterClass
注解的方法)不在内部运行测试管理的事务。如果你需要在事务内的套件级或类级生命周期方法中执行代码,则可能希望将相应的
PlatformTransactionManager
注入测试类,然后将其与TransactionTemplate
一起用于程序化事务管理。
请注意,AbstractTransactionalJUnit4SpringContextTests和AbstractTransactionalTestNGSpringContextTests已预先配置为在类级别提供事务支持。
下面的示例演示了为基于Hibernate
的UserRepository
编写集成测试的常见方案:
@SpringJUnitConfig(TestConfig.class) @Transactional class HibernateUserRepositoryTests { @Autowired HibernateUserRepository repository; @Autowired SessionFactory sessionFactory; JdbcTemplate jdbcTemplate; @Autowired void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } @Test void createUser() { // track initial state in test database: final int count = countRowsInTable("user"); User user = new User(...); repository.save(user); // Manual flush is required to avoid false positive in test sessionFactory.getCurrentSession().flush(); assertNumUsers(count + 1); } private int countRowsInTable(String tableName) { return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName); } private void assertNumUsers(int expected) { assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user")); } }
如“事务回滚和提交行为”中所述,运行createUser()
方法后无需清理数据库,因为对数据库所做的任何更改都会由TransactionalTestExecutionListener
自动回滚。
参考代码:
org.liyong.test.annotation.test.spring.TransactionMergeUserRepositoryTests
和org.liyong.test.annotation.test.spring.TransactionUserRepositoryTests
事物回滚和提交
默认情况下,测试事务将在测试完成后自动回滚;但是,可以通过@Commit
和@Rollback
注解声明性地配置事务@Commit
和@Rollback
行为。有关更多详细信息,请参见注解支持部分中的对应条目。
参考代码:
org.liyong.test.annotation.test.spring.AnnotationTransactionManagementTests
编程式事物管理
你可以使用TestTransaction
中的静态方法以编程方式与测试管理的事务进行交互。例如,可以在测试方法中、方法之前和方法之后使用TestTransaction
来启动或结束当前的测试托管事务,或配置当前的测试托管事务以进行回滚或提交。每当启用TransactionalTestExecutionListener
时,都会自动提供对TestTransaction
的支持。下面的示例演示了TestTransaction
的某些功能。有关更多详细信息,请参见javadoc中的TestTransaction。
@ContextConfiguration(classes = TestConfig.class) public class ProgrammaticTransactionManagementTests extends AbstractTransactionalJUnit4SpringContextTests { @Test public void transactionalTest() { // assert initial state in test database: assertNumUsers(2); deleteFromTables("user"); // changes to the database will be committed! TestTransaction.flagForCommit(); TestTransaction.end(); assertFalse(TestTransaction.isActive()); assertNumUsers(0); TestTransaction.start(); // perform other actions against the database that will // be automatically rolled back after the test completes... } protected void assertNumUsers(int expected) { assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user")); } }
参考代码:
org.liyong.test.annotation.test.spring.ProgrammaticTransactionManagementTests
在事务外运行代码
有时,你可能需要在事务测试方法之前或之后但在事务上下文之外执行某些代码。例如,在运行测试之前验证初始数据库状态或在测试运行之后验证预期的事务提交行为(如果测试已配置为提交事务)。
对于此类情况,TransactionalTestExecutionListener
支持@BeforeTransaction
和@AfterTransaction
注解。你可以使用这些注解之一来注释测试类中的任何void
方法或测试接口中的任何void
默认方法,并且TransactionalTestExecutionListener
可以确保你的before
事务方法或after
事务方法在适当的时间运行。
任何
before
方法(例如,以JUnit Jupiter的@BeforeEach
注解的方法)和任何after
方法(例如,以JUnit Jupiter的@AfterEach
注解的方法)都在事务中运行。此外,对于未配置为在事务内运行的测试方法,不会运行带有@BeforeTransaction
或@AfterTransaction
注解的方法。
配置事物管理器
TransactionalTestExecutionListener
期望在Spring ApplicationContext
中为测试定义一个PlatformTransactionManager
bean。如果测试的ApplicationContext
中存在PlatformTransactionManager
的多个实例,则可以使用@Transactional("myTxMgr")
或@Transactional(transactionManager ="myTxMgr")
声明限定符,或者可以通过@Configuration
类实现TransactionManagementConfigurer
。有关用于在测试的ApplicationContext
中查找事务管理器的算法的详细信息,请查阅Javadoc中的TestContextTransactionUtils.retrieveTransactionManager()。
演示所有与事务相关的注解
以下基于JUnit Jupiter的示例显示了一个虚拟的集成测试方案,该方案突出显示了所有与事务相关的注解。该示例并非旨在演示最佳实践,而是演示如何使用这些注解。有关更多信息和配置示例,请参见注解支持部分。@Sql
的事务管理包含一个附加示例,该示例使用@Sql
执行具有默认事务回滚语义的声明性SQL脚本。下面的示例显示了相关的注解。
@SpringJUnitConfig @Transactional(transactionManager = "txMgr") @Commit class FictitiousTransactionalTest { @BeforeTransaction void verifyInitialDatabaseState() { // logic to verify the initial state before a transaction is started } @BeforeEach void setUpTestDataWithinTransaction() { // set up test data within the transaction } @Test // overrides the class-level @Commit setting @Rollback void modifyDatabaseWithinTransaction() { // logic which uses the test data and modifies database state } @AfterEach void tearDownWithinTransaction() { // execute "tear down" logic within the transaction } @AfterTransaction void verifyFinalDatabaseState() { // logic to verify the final state after transaction has rolled back } }
测试ORM代码时避免误报
当你测试操纵
Hibernate
会话或JPA持久性上下文状态的应用程序代码时,请确保在运行该代码的测试方法中刷新基础工作单元。未能刷新基本工作单元可能会产生误报:你的测试通过了,但是相同的代码在实际的生产环境中引发了异常。请注意,这适用于任何维护内存中工作单元的ORM框架。在下面的基于Hibernate的示例测试用例中,一种方法演示了误报,另一种方法正确地公开了刷新会话的结果:// ... @Autowired SessionFactory sessionFactory; @Transactional @Test // no expected exception! public void falsePositive() { updateEntityInHibernateSession(); // False positive: an exception will be thrown once the Hibernate // Session is finally flushed (i.e., in production code) } @Transactional @Test(expected = ...) public void updateWithSessionFlush() { updateEntityInHibernateSession(); // Manual flush is required to avoid false positive in test sessionFactory.getCurrentSession().flush(); } // ...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
以下示例显示了JPA的匹配方法:
// ... @PersistenceContext EntityManager entityManager; @Transactional @Test // no expected exception! public void falsePositive() { updateEntityInJpaPersistenceContext(); // False positive: an exception will be thrown once the JPA // EntityManager is finally flushed (i.e., in production code) } @Transactional @Test(expected = ...) public void updateWithEntityManagerFlush() { updateEntityInJpaPersistenceContext(); // Manual flush is required to avoid false positive in test entityManager.flush(); } // ...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
在针对关系数据库编写集成测试时,执行SQL脚本来修改数据库模式或将测试数据插入表中通常是有益的。spring-jdbc
模块支持在加载Spring ApplicationContext
时通过执行SQL脚本来初始化嵌入式数据库或现有数据库。有关详细信息,请参见嵌入式数据库支持和使用嵌入式数据库测试数据访问逻辑。
尽管在加载ApplicationContext
时初始化一次数据库以进行测试非常有用,但是有时在集成测试过程中能够修改数据库至关重要。以下各节说明在集成测试期间如何以编程方式和声明方式执行SQL脚本。
编程式地执行SQL脚本
Spring提供了以下选项,用于在集成测试方法中以编程方式执行SQL脚本。
org.springframework.jdbc.datasource.init.ScriptUtils
org.springframework.jdbc.datasource.init.ResourceDatabasePopulator
org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests
org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests
ScriptUtils
提供了一组处理SQL脚本的静态实用程序方法,主要用于框架内的内部使用。但是,如果你需要完全控制SQL脚本的解析和执行方式,那么ScriptUtils
可能比后面描述的其他一些替代方案更适合你的需要。有关更多细节,请参阅javadoc以了解ScriptUtils
中的各个方法。
ResourceDatabasePopulator
提供了一个基于对象的API,用于使用在外部资源中定义的SQL脚本以编程方式填充、初始化或清理数据库。ResourceDatabasePopulator
提供了一些选项,用于配置解析和运行脚本时使用的字符编码、语句分隔符、注释分隔符和错误处理标志。每个配置选项都有一个合理的默认值。有关默认值的详细信息,请参阅javadoc。要运行ResourceDatabasePopulator
中配置的脚本,可以调用populate(Connection)
方法对java.sql.Connection
执行填充器,或者execute(DataSource)
方法对javax.sql.DataSource
执行填充器。以下示例为测试schema
和测试数据指定SQL脚本,将语句分隔符设置为@@
,然后针对数据源执行脚本:
@Test
void databaseTest() {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScripts(
new ClassPathResource("test-schema.sql"),
new ClassPathResource("test-data.sql"));
populator.setSeparator("@@");
populator.execute(this.dataSource);
// execute code that uses the test schema and data
}
请注意,ResourceDatabasePopulator
内部委托给ScriptUtils
来解析和运行SQL脚本。同样,AbstractTransactionalJUnit4SpringContextTests和AbstractTransactionalTestNGSpringContextTests中的executeSqlScript(..)
方法在内部使用ResourceDatabasePopulator
运行SQL脚本。有关各种executeSqlScript(..)
方法的详细信息,请参阅javadoc。
参考代码:
org.liyong.test.annotation.test.spring.TransactionUserRepositoryTests
使用@Sql声明式地执行SQL脚本
除了上述用于以编程方式运行SQL脚本的机制之外,你还可以在Spring TestContext
框架中声明性地配置SQL脚本。具体来说,你可以在测试类或测试方法上声明@Sql
注解,以配置单独的SQL语句或应在集成测试方法之前或之后针对给定数据库运行的SQL脚本的资源路径。@Sql
的支持由SqlScriptsTestExecutionListener
提供,默认情况下启用。
方法级别的
@Sql
声明默认情况下覆盖类级别的声明。从Spring框架5.2开始,可以通过@SqlMergeMode
为每个测试类或每个测试方法配置此行为。有关更多详细信息,请参见使用@SqlMergeMode合并和覆盖配置。
路径资源语义
每个路径都被解释为Spring资源。文本路径(例如,schema.sql
)被视为相对于定义测试类的包的类路径资源。以斜杠开头的路径被视为绝对类路径资源(例如,/org/example/schema.sql
)。通过使用指定的资源协议加载引用URL的路径(例如,以classpath:
、file:
、http:
为前缀的路径)。以下示例显示了如何在基于JUnit Jupiter的集成测试类中的类级别和方法级别使用@Sql
:
@SpringJUnitConfig
@Sql("/test-schema.sql")
class DatabaseTests {
@Test
void emptySchemaTest() {
// execute code that uses the test schema without any test data
}
@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"})
void userTest() {
// execute code that uses the test schema and test data
}
}
参考代码:
org.liyong.test.annotation.test.spring.InferredDataSourceTransactionalSqlScriptsTests
默认脚本检测
如果未指定任何SQL脚本或语句,则根据声明@Sql
的位置来尝试检测默认脚本。如果无法检测到默认值,则抛出IllegalStateException
。
com.example.MyTest
,则相应的默认脚本是classpath:com/example/MyTest.sql
。testMethod()
且在com.example.MyTest
类中定义,则相应的默认脚本为classpath:com/example/MyTest.testMethod.sql
。声明多个@Sql集合
如果需要为给定的测试类或测试方法配置多组SQL脚本,但使用不同的语法配置、不同的错误处理规则或每组不同的执行阶段,则可以声明@Sql
的多个实例。使用Java 8,你可以将@Sql
用作可重复注解。否则,你可以使用@SqlGroup
注解作为显式容器来声明@Sql
的多个实例。
下面的示例演示如何将@Sql
用作Java 8的可重复注解:
@Test
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`"))
@Sql("/test-user-data.sql")
void userTest() {
// execute code that uses the test schema and test data
}
在前面的示例中呈现的方案中,test-schema.sql
脚本对单行注解使用了不同的语法。
除了@Sql
声明在@SqlGroup
中分组在一起之外,以下示例与上述示例相同。在Java 8及更高版本中,使用@SqlGroup
是可选的,但是你可能需要使用@SqlGroup
与其他JVM语言(例如Kotlin)兼容。
@Test
@SqlGroup({
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
@Sql("/test-user-data.sql")
)}
void userTest() {
// execute code that uses the test schema and test data
}
脚本执行阶段
默认情况下,SQL脚本在相应的测试方法之前执行。但是,如果需要在测试方法之后运行一组特定的脚本(例如,清理数据库状态),则可以使用@Sql
中的executionPhase
属性,如以下示例所示:
@Test
@Sql(
scripts = "create-test-data.sql",
config = @SqlConfig(transactionMode = ISOLATED)
)
@Sql(
scripts = "delete-test-data.sql",
config = @SqlConfig(transactionMode = ISOLATED),
executionPhase = AFTER_TEST_METHOD
)
void userTest() {
// execute code that needs the test data to be committed
// to the database outside of the test's transaction
}
请注意,分别从Sql.TransactionMode
和Sql.ExecutionPhase
静态导入了ISOLATED
和AFTER_TEST_METHOD
。
@SqlConfig脚本配置
你可以使用@SqlConfig
注解配置脚本解析和错误处理。当在集成测试类上声明为类级别的注解时,@SqlConfig
充当测试类层次结构中所有SQL脚本的全局配置。通过使用@Sql
注解的config
属性直接声明时,@SqlConfig
用作封闭的@Sql
注解中声明的SQL脚本的本地配置。@SqlConfig
中的每个属性都有一个隐式默认值,该默认值记录在相应属性的javadoc中。不幸的是,由于Java语言规范中为注解属性定义了规则,因此无法将null
值分配给注解属性。因此,为了支持对继承的全局配置的覆盖,@SqlConfig
属性的显式默认值为“”
(对于字符串)、{}
(对于数组)或DEFAULT
(对于枚举)。这种方法允许@SqlConfig
的本地声明通过提供除“”
、{}
或DEFAULT
之外的值来有选择地覆盖@SqlConfig
的全局声明中的各个属性。只要本地@SqlConfig
属性不提供除“”
、{}
或DEFAULT
以外的显式值,就会继承全局@SqlConfig
属性。因此,显式本地配置将覆盖全局配置。
@Sql
和@SqlConfig
提供的配置选项与ScriptUtils
和ResourceDatabasePopulator
支持的配置选项等效,但是,是<jdbc:initialize-database />
XML名称空间元素提供的配置选项的超集。有关详细信息,请参见@Sql和@SqlConfig中各个属性的javadoc。
@Sql事物管理
默认情况下,SqlScriptsTestExecutionListener
会为使用@Sql
配置的脚本推断所需的事务语义。具体来说,SQL脚本不是在没有事务的情况下运行,而是在现有的Spring管理的事务中运行(例如,由TransactionalTestExecutionListener
管理的事务,以@Transactional
进行注解的测试),或者在隔离的事务中运行,具体取决于transactionMode
的配置值@SqlConfig
中的属性,以及测试的ApplicationContext
中是否存在PlatformTransactionManager
。但是,作为最低要求,测试的ApplicationContext
中必须存在一个javax.sql.DataSource
。
如果SqlScriptsTestExecutionListener
用来检测DataSource
和PlatformTransactionManager
并推断事务语义的算法不符合你的需求,则可以通过设置@SqlConfig
的dataSource
和transactionManager
属性来指定显式名称。此外,你可以通过设置@SqlConfig
的transactionMode
属性来控制事务传播行为(例如,是否应在隔离的事务中运行脚本)。虽然彻底讨论事务管理的所有支持的选项@Sql
超出了这个范围参考手册,@SqlConfig 和SqlScriptsTestExecutionListener javadoc提供详细信息,和下面的示例显示了一个典型的测试场景中,使用JUnit Jupiter和事务与@Sq
l测试:
@SpringJUnitConfig(TestDatabaseConfig.class) @Transactional class TransactionalSqlScriptsTests { final JdbcTemplate jdbcTemplate; @Autowired TransactionalSqlScriptsTests(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } @Test @Sql("/test-data.sql") void usersTest() { // verify state in test database: assertNumUsers(2); // execute code that uses the test data... } int countRowsInTable(String tableName) { return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName); } void assertNumUsers(int expected) { assertEquals(expected, countRowsInTable("user"), "Number of rows in the [user] table."); } }
请注意,在运行usersTest()方
法之后,无需清理数据库,因为对数据库所做的任何更改(在test
方法中或在/test-data.sql
脚本中)都将自动回滚。 TransactionalTestExecutionListener
(有关详细信息,请参见事务管理)。
使用@SqlMergeMode合并和覆盖配置
从Spring框架5.2开始,可以将方法级@Sql
声明与类级声明合并。例如,这允许你为每个测试类提供一次数据库schema
的配置或一些常见的测试数据,然后为每种测试方法提供特定于用例的其他测试数据。若要启用@Sql
合并,请使用@SqlMergeMode(MERGE)
注解测试类或测试方法。若要禁用特定测试方法(或特定测试子类)的合并,可以通过@SqlMergeMode(OVERRIDE)
切换回默认模式。有关示例和更多详细信息,请查阅@SqlMergeMode注解文档部分。
Spring框架5.0引入了对使用Spring TestContext
框架时在单个JVM中并行执行测试的基本支持。通常,这意味着大多数测试类或测试方法可以并行执行,而无需更改测试代码或配置。
有关如何设置并行测试执行的详细信息,请参见你的测试框架,构建工具或IDE的文档。
请记住,将并发引入测试套件可能会导致意外的副作用,奇怪的运行时行为以及间歇性或看似随机失败的测试。因此,Spring团队为何时不并行执行测试提供了以下一般指导。
如果测试符合以下条件,则不要并行执行测试:
@DirtiesContext
支持。@MockBean
或@SpyBean
支持。@FixMethodOrder
支持或旨在确保测试方法按特定顺序运行的任何测试框架功能。但是请注意,如果整个测试类是并行执行的,则此方法不适用。如果并行测试执行失败,并有异常指出当前测试的
ApplicationContext
不再处于活动状态,则这通常意味着ApplicationContext
已从ContextCache
中的另一个线程中删除。这可能是由于使用
@DirtiesContext
或由于从ContextCache
自动驱逐。如果@DirtiesContext
是罪魁祸首,则需要找到一种避免使用@DirtiesContext
的方法,或者从并行执行中排除此类测试。如果已超过ContextCache
的最大大小,则可以增加缓存的最大大小。有关详细信息,请参见上下文缓存的讨论。
Spring
TestContext
框架中的并行测试执行只有在底层的TestContext
实现提供了副本构造函数的情况下才可能执行,如TestContext的javadoc中所述。Spring中使用的DefaultTestContext
提供了这样的构造函数。但是,如果你使用提供自定义TestContext
实现的第三方库,则需要验证它是否适合并行测试执行。
本节描述了支持Spring TestContext
框架的各种类。
Spring JUnit 4 Runner
Spring TestContext
框架通过自定义运行程序(在JUnit 4.12或更高版本上受支持)提供了与JUnit 4的完全集成。通过使用@RunWith(SpringJUnit4ClassRunner.class)
或简写的@RunWith(SpringRunner.class)
注释测试类,开发人员可以实现基于JUnit 4的标准单元测试和集成测试,同时获得TestContext
框架的优势,例如对加载应用程序上下文、测试实例的依赖关系注入、事务性测试方法执行等。如果你想将Spring TestContext
框架与替代运行程序(例如JUnit 4的Parameterized
运行程序)或第三方运行程序(例如MockitoJUnitRunner
)一起使用,则可以选择使用Spring对JUnit规则的支持。
以下代码清单显示了配置测试类以与自定义Spring Runner一起运行的最低要求:
@RunWith(SpringRunner.class)
@TestExecutionListeners({})
public class SimpleTest {
@Test
public void testMethod() {
// execute test logic...
}
}
在前面的示例中,为@TestExecutionListeners
配置了一个空列表以禁用默认监听器,否则将需要通过@ContextConfiguration
配置ApplicationContext
。
Spring JUnit4规则
org.springframework.test.context.junit4.rules
包提供以下JUnit 4规则(在JUnit 4.12或更高版本上受支持):
SpringClassRule
SpringMethodRule
SpringClassRule
是一个JUnit TestRule
,它支持Spring TestContext
框架的类级功能,而SpringMethodRule
是一个JUnit MethodRule
,它支持Spring TestContext
框架的实例级和方法级功能。
与SpringRunner
相比,Spring的基于规则的JUnit支持具有独立于任何org.junit.runner.Runner
实现的优点,因此可以与现有的替代运行器(例如JUnit 4的Parameterized
)或第三方结合使用运行器(例如MockitoJUnitRunner)。
为了支持TestContext
框架的全部功能,必须将SpringClassRule
与SpringMethodRule
结合使用。以下示例显示了在集成测试中声明这些规则的正确方法:
// Optionally specify a non-Spring Runner via @RunWith(...)
@ContextConfiguration
public class IntegrationTest {
@ClassRule
public static final SpringClassRule springClassRule = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@Test
public void testMethod() {
// execute test logic...
}
}
JUnit 4 Support支持类
org.springframework.test.context.junit4
包为基于JUnit 4的测试用例提供了以下支持类(在JUnit 4.12或更高版本上受支持):
AbstractJUnit4SpringContextTests
AbstractTransactionalJUnit4SpringContextTests
AbstractJUnit4SpringContextTests
是抽象的基础测试类,该类将Spring TestContext
框架与JUnit 4环境中的显式ApplicationContext
测试支持集成在一起。扩展AbstractJUnit4SpringContextTests
时,可以访问protected applicationContext
实例变量,该变量可用于执行显式bean查找或测试整个上下文的状态。
AbstractTransactionalJUnit4SpringContextTests
是AbstractJUnit4SpringContextTests
的抽象事务扩展,为JDBC访问添加了一些便利功能。此类期望在ApplicationContext
中定义一个javax.sql.DataSource
bean和PlatformTransactionManager
bean。扩展AbstractTransactionalJUnit4SpringContextTests
时,可以访问protected jdbcTemplate
实例变量,该实例变量可用于运行SQL语句来查询数据库。你可以在运行与数据库相关的应用程序代码之前和之后使用此类查询来确认数据库状态,并且Spring确保此类查询在与应用程序代码相同的事务范围内运行。与ORM工具一起使用时,请确保避免误报。如JDBC测试支持中所述,AbstractTransactionalJUnit4SpringContextTests
还提供了方便的方法,这些方法通过使用上述的jdbcTemplate
委托给JdbcTestUtils
中的方法。此外,AbstractTransactionalJUnit4SpringContextTests
提供了executeSqlScript(..)
方法,用于针对已配置的DataSource
运行SQL脚本。
这些类为扩展提供了便利。如果你不希望将测试类绑定到特定于Spring的类层次结构,则可以使用
@RunWith(SpringRunner.class)
或Spring的JUnit规则来配置自己的自定义测试类。
JUnit Jupiter的SpringExtension
Spring TestContext
框架提供了与JUnit 5中引入的JUnit Jupiter测试框架的完全集成。通过使用@ExtendWith(SpringExtension.class)
注解测试类,你可以实现基于JUnit Jupiter的标准单元测试和集成测试,并同时获得TestContext
框架的好处,例如支持加载应用程序上下文、测试实例的依赖注入、事务性测试方法执行等等。
此外,得益于JUnit Jupiter中丰富的扩展API,Spring在Spring支持JUnit 4和TestNG的功能集之外提供了以下功能:
测试构造函数、测试方法和测试生命周期回调方法的依赖注入。有关更多详细信息,请参见使用SpringExtension进行依赖注入。
对基于SpEL表达式、环境变量、系统属性等的条件测试执行的强大支持。有关更多详细信息和示例,请参见Spring JUnit Jupiter测试注解中有关@EnabledIf
和@DisabledIf
的文档。
自定义组合注解,结合了Spring和JUnit Jupiter的注解。有关更多详细信息,请参见“元注解支持测试”中的@TransactionalDevTestConfig
和@TransactionalIntegrationTest
示例。
以下代码清单显示如何配置测试类以将SpringExtension
与@ContextConfiguration
结合使用:
// Instructs JUnit Jupiter to extend the test with Spring support.
@ExtendWith(SpringExtension.class)
// Instructs Spring to load an ApplicationContext from TestConfig.class
@ContextConfiguration(classes = TestConfig.class)
class SimpleTests {
@Test
void testMethod() {
// execute test logic...
}
}
由于你还可以在JUnit 5中将注解用作元注解,因此Spring提供了@SpringJUnitConfig
和@SpringJUnitWebConfig
组成的注解,以简化测试ApplicationContext
和JUnit Jupiter的配置。
以下示例使用@SpringJUnitConfig
减少前一示例中使用的配置量:
// Instructs Spring to register the SpringExtension with JUnit
// Jupiter and load an ApplicationContext from TestConfig.class
@SpringJUnitConfig(TestConfig.class)
class SimpleTests {
@Test
void testMethod() {
// execute test logic...
}
}
同样,以下示例使用@SpringJUnitWebConfig
创建用于JUnit Jupiter的WebApplicationContext
:
// Instructs Spring to register the SpringExtension with JUnit
// Jupiter and load a WebApplicationContext from TestWebConfig.class
@SpringJUnitWebConfig(TestWebConfig.class)
class SimpleWebTests {
@Test
void testMethod() {
// execute test logic...
}
}
有关更多详细信息,请参见Spring JUnit Jupiter测试注解中有关@SpringJUnitConfig
和@SpringJUnitWebConfig的文档。
使用SpringExtension
依赖注入
SpringExtension
从JUnit Jupiter实现了ParameterResolver扩展API,它使Spring可以为测试构造函数、测试方法和测试生命周期回调方法提供依赖项注入。
具体来说,SpringExtension
可以将来自测试ApplicationContext
的依赖项注入到使用@BeforeAll
、@AfterAll
、@BeforeEach
、@AfterEach
、@Test
、@RepeatedTest
、@ParameterizedTest
等标记的测试构造函数和方法中。
构造函数注入
如果JUnit Jupiter测试类的构造函数中的特定参数为ApplicationContext
类型(或其子类型)、或者使用@Autowired
、@Qualifier
或@Value进行注解或元注解,则Spring会为此特定值注入值参数与测试的ApplicationContext
中的相应bean或值。
如果认为构造函数是可自动装配的,则还可以将Spring配置为自动连接测试类构造函数的所有参数。如果满足以下条件之一(按优先顺序),则认为构造函数是可自动构造的。
@Autowired
注解。@TestConstructor
在autowireMode
属性设置为ALL的情况下出现在测试类中或以元形式出现。有关使用@TestConstructor以及如何更改全局测试构造函数自动装配模式的详细信息,请参见@TestConstructor
。
如果测试类的构造函数被认为是可自动装配的,则Spring负责解析构造函数中所有参数的参数。因此,在JUnit Jupiter中注册的其他
ParameterResolver
不能解析此类构造函数的参数。如果在测试方法之前或之后使用
@DirtiesContext
关闭测试的ApplicationContext
,则不得与JUnit Jupiter的@TestInstance(PER_CLASS)
支持一起使用针对测试类的构造函数注入。原因是
@TestInstance(PER_CLASS)
指示JUnit Jupiter在测试方法调用之间缓存测试实例。因此,测试实例将保留对最初从随后已关闭的ApplicationContext
注入的bean的引用。由于在这种情况下测试类的构造函数将仅被调用一次,因此依赖注入不会再次发生,并且后续测试将与关闭的ApplicationContext
中的bean进行交互,这可能会导致错误。要将
@DirtiesContext
与@TestInstance(PER_CLASS)
一起用于“测试方法之前
”或“测试方法之后
”模式,必须配置通过字段或setter
注入提供的Spring依赖项,以便可以在测试之间重新注入它们方法调用。
在下面的示例中,Spring将从TestConfig.class
加载的ApplicationContext
中的OrderService
bean注入OrderServiceIntegrationTests
构造函数中。
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
private final OrderService orderService;
@Autowired
OrderServiceIntegrationTests(OrderService orderService) {
this.orderService = orderService;
}
// tests that use the injected OrderService
}
请注意,此功能使测试依赖项是最终的,因此是不可变的。
如果spring.test.constructor.autowire.mode
属性是all
(请参阅@TestConstructor),则可以在上一个示例中省略构造函数上@Autowired
的声明,从而得到以下结果。
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
private final OrderService orderService;
OrderServiceIntegrationTests(OrderService orderService) {
this.orderService = orderService;
}
// tests that use the injected OrderService
}
方法注入
如果JUnit Jupiter测试方法或测试生命周期回调方法中的参数属于ApplicationContext
类型(或其子类型),或者使用@Autowired
、@Qualifier
或@Value
进行注解或元注解,则Spring会为此注入值。特定参数以及来自测试的ApplicationContext
的相应bean。
在下面的示例中,Spring从TestConfig.class
加载的ApplicationContext
中将OrderService
注入到deleteOrder()
测试方法中:
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
@Test
void deleteOrder(@Autowired OrderService orderService) {
// use orderService from the test's ApplicationContext
}
}
由于JUnit Jupiter中ParameterResolver
支持的强大功能,你不仅可以从Spring中,而且可以从JUnit Jupiter本身或其他第三方扩展中,将多个依赖项注入到单个方法中。
下面的示例演示如何让Spring和JUnit Jupiter同时将依赖项注入到placeOrderRepeatedly()
测试方法中。
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
@RepeatedTest(10)
void placeOrderRepeatedly(RepetitionInfo repetitionInfo,
@Autowired OrderService orderService) {
// use orderService from the test's ApplicationContext
// and repetitionInfo from JUnit Jupiter
}
}
注意,通过使用JUnit Jupiter中的@RepeatedTest
,测试方法可以访问RepetitionInfo
。
TestNG支持类
org.springframework.test.context.testng
包为基于TestNG的测试用例提供以下支持类:
AbstractTestNGSpringContextTests
AbstractTransactionalTestNGSpringContextTests
AbstractTestNGSpringContextTests
是抽象的基础测试类,该类将Spring TestContext
框架与TestNG环境中的显式ApplicationContext
测试支持集成在一起。扩展AbstractTestNGSpringContextTests
时,可以访问protected applicationContext
实例变量,该变量可用于执行显式bean查找或测试整个上下文的状态。
AbstractTransactionalTestNGSpringContextTests
是AbstractTestNGSpringContextTests
的抽象事务扩展,为JDBC访问添加了一些便利功能。此类期望在ApplicationContext
中定义一个javax.sql.DataSource
bean和PlatformTransactionManager
bean。扩展AbstractTransactionalTestNGSpringContextTests
时,可以访问protected jdbcTemplate
实例变量,该实例变量可用于执行SQL语句来查询数据库。你可以在运行与数据库相关的应用程序代码之前和之后使用此类查询来确认数据库状态,并且Spring确保此类查询在与应用程序代码相同的事务范围内运行。与ORM工具一起使用时,请确保避免误报。如JDBC测试支持中所述,AbstractTransactionalTestNGSpringContextTests
还提供了方便的方法,这些方法通过使用上述的jdbcTemplate
委托给JdbcTestUtils
中的方法。此外,AbstractTransactionalTestNGSpringContextTests
提供了executeSqlScript(..)
方法,用于针对已配置的DataSource
运行SQL脚本。
这些类为扩展提供了便利。如果你不希望将测试类绑定到特定于Spring的类层次结构,则可以使用
@ContextConfiguration
、@TestExecutionListeners
等来配置自己的自定义测试类,并通过使用TestContextManager
手动检测测试类。有关如何检测测试类的示例,请参见AbstractTestNGSpringContextTests
的源代码。
个人从事金融行业,就职过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就职于某银行负责统一支付系统建设。自身对金融行业有强烈的爱好。同时也实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域。同时也热衷于技术分享创立公众号和博客站点对知识体系进行分享。
博客地址:http://youngitman.tech
CSDN:https://blog.csdn.net/liyong1028826685
微信公众号:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。