您的位置:首页 > 编程语言 > Java开发

Spring中文文档翻译Integration Testing(1)

2017-10-12 14:35 369 查看

15 Integration Testing

15.1 概述

在集成测试中,很重要的一点是,不需要启动应用程序或不需要链接其他企业架构。这将会使你能够测试以下的内容:

- spring ioc的正确性

- 使用JDBC或者其他ORM工具访问数据库。包括SQL Statement正确性,Hibernate查询,JPA Entity 映射,等等。

Spring Framework在spring-test 模块中对集成测试提供了一级支持。JAR文件的名称需要包括Release版本号。版本号可能按照org.springframework.test的格式命名。spring-test JAR中包括包路径:org.springframework.test,下面包含了很多使用spring container测试的有用类。这些测试不依赖于应用server或者其他部署环境。这些测试的运行速度要慢于单元测试,但是要快于Selenium 测试或依赖于应用server部署的远程测试。

从spring2.5开始,单元测试和集成测试开始支持注解驱动 Spring TestContext Framework。TestContext 是实际使用的框架,这样就可以和JUnit,TestNGO等测试框架协作使用。

15.2 集成测试目标

Spring的集成测试主要目标包括以下几点:

- 在测试执行期间,管理Spring IOC容器缓存

- 提供依赖注入

- 提供事务管理

- 支持开发者在集成测试用例中使用spring的基础类

余下的章节描述了以上的各个目标,并提供了实现和配置的细节。

15.2.1 Context管理和缓存

Spring TestContext Framework会持续加载ApplicationContexts 和WebApplicationContexts,并进行缓存。缓存的支持是很重要的一点因为准备时间可能成为一个问题。不是因为spring的启动,而是spring container中对象的初始化需要耗费时间。比如说,一个工程有50到100的Hibernate映射文件,预计消耗10到20秒的时间加载这些文件。如果每个测试用例都需要重新加载这些文件,测试用例的执行将会变得很慢。

测试类可以声明一组xml或者groovy配置文件(通常在classpath中),或者一组注解类。

默认情况下,ApplicationContext 会加载一次,每个test共用这个ApplicationContext。这样每个测试集只会耗费一次初始化时间,顺序test执行会更快。在本文中,测试集意味着在一个JVM中的测试。比如说,Ant,Maven或者Gradle编译一个工程或模块中的所有测试用例。可能存在其他的情况,Context失效或者需要重新加载。比如说修改一个bean的定义或者一个应用对象的状态。TestContext框架可以配置成重新加载配置且在执行下一个测试之前重新创建Context。

TestContext框架文档:Section 15.5.4, “Context management” and the section called “Context caching”

15.2.2 测试固定组件中的依赖注入

当TestContext 框架加载你的应用程序上下文,它会通过依赖注入选择性的配置测试类的实例。这提供了一种便利的机制,使用应用程序中预先配置的bean来构建测试固定组件。一个明显的好处是你可以在不同的测试情景下复用应用程序上下文(比如配置spring管理的对象图,事物代理,DataSource等)。这样避免了每个测试用例中复杂测试固定组件的重复加载。

举个栗子:),考虑一种场景,我们有一个类:HibernateTitleRepository。这个类实现了获取Title域下的实体获取。我们想要测试以下的部分:

- spring 配置。基本的说,HibernateTitleRepository bean所有的配置信息是否呈现且正确。

- Hibernate 映射文件配置:是否所有的都正确映射,都能在需要时正确的实现懒加载。

- HibernateTitleRepository逻辑:这个类的实例是否和预期的一样。

了解测试固定组件的依赖注入 TestContext framework

15.2.3 事务管理

在测试过程中访问真实的数据库并对持久化数据做了更改,是一个公共性的问题。即使你使用了一个开发库,数据的更改会影响以后的测试。许多的操作,包括插入或修改持久化数据,不应该在事物外进行。

TestContext框架解决了这个问题。默认情况下,框架会为每个测试用例创建一个事物,并在结束的时候进行回滚。只需要在存在事物的假设下完成代码。如果在测试中调用事物代理的对象,根据事物的配置语法,他们会正确运行。另外,如果一个测试在事物管理的情况下删除了数据表,默认情况下事物会回滚,数据库会回到运行测试用例之前的状态。框架通过PlatformTransactionManager bean实现了事物的支持。

如果希望一个事物能够提交,可以使用@Commit注解。

了解事物的详情

15.2.4 集成测试提供的类

Spring TestContext 框架为了简化集成测试的书写,提供了一些抽象类。这些基础测试类为框架提供了hook,也提供了一些方法和变量,可以让你轻松获取以下信息:

- ApplicationContext。可以查询bean和检查context的状态。

- JdbcTemplate,执行sql访问数据库。这些访问可以用于在执行数据库相关代码之前或者之后查询数据库状态,并且spring保证这些sql的事物和应用code的事物是一样的。在和ORM工具一起使用的时候,请确认false positives.

另外,在项目中,你可以创建自己的,应用级别的实例变量和方法。

查询提供的类列表

15.3 JDBC测试支持

包路径org.springframework.test.jdbc 下包含JdbcTestUtils,是JDBC实用函数集合,简化标准数据库测试场景。JdbcTestUtils 主要的方法:

- countRowsInTable。表的行数

- countRowsInTableWhere。带where条件查询表的行数。

- deleteFromTables。删除表中所有数据。

- deleteFromTableWhere。删除表中满足where条件的数据。

- dropTables。删除表。

注意,AbstractTransactionalJUnit4SpringContextTests 和AbstractTransactionalTestNGSpringContextTests提供了访问JdbcTestUtils的快捷方法。


spring-jdbc模块支持配置和加载内嵌数据库,可以在需要数据库的集成测试中使用。

可以在 Section 19.8, “Embedded database support” and Section 19.8.5, “Testing data access logic with an embedded database” 查看详细信息

15.4 注解

Spring Framework提供了spring专用注解,可以在TestContext 的单元测试和集成测试中使用。可以通过javadoc进行详细信息的阅读,比如默认属性值,属性别名等。

@BootstrapWith

类级别的注解,配置Spring TestContext Framework启动信息。BootstrapWith可以用来配置自定义TestContextBootstrapper。可以在 Bootstrapping the TestContext framework章节阅读详细信息。

@ContextConfiguration

类级别注解,定义怎样为集成测试加载和配置ApplicationContext。需要关注的是,ContextConfiguration 声明了应用程序context资源文件的位置信息和用于加载context所使用的注解类。

资源文件通常是classpath下的xml配置文件,或者groovy脚本文件;注解类通常是@Configuration配置的类。

然而,资源文件也可以是文件系统中的文件或脚本,配置文件可以是component配置的类。

@ContextConfiguration("/test-config.xml")
public class XmlApplicationContextTests {
// class body...
}


@ContextConfiguration(classes = TestConfig.class)
public class ConfigClassApplicationContextTests {
// class body...
}


@ContextConfiguration也可以用于声明ApplicationContextInitializer类。

@ContextConfiguration(initializers = CustomContextIntializer.class)
public class ContextInitializerTests {
// class body...
}


也可以用于声明ContextLoader策略。注意,一般不需要配置loader,默认的loader已经可以支持locations,注解的classes。

@ContextConfiguration(locations = "/test-context.xml", loader = CustomContextLoader.class)
public class CustomLoaderXmlApplicationContextTests {
// class body...
}


注意,@ContextConfiguration支持继承。

可以在 Section 15.5.4, “Context management”进行详细阅读。

@WebAppConfiguration

类级别注解,声明测试用例加载的ApplicationContext是WebApplicationContext。使用”file:src/main/webapp”作为web应用的根路径(resource base path)。资源基本路径被用来创建MockServletContext。MockServletContext为测试用例的WebApplicationContext提供ServletContext 服务。

@ContextConfiguration
@WebAppConfiguration
public class WebAppTests {
// class body...
}


为了覆盖默认设置,可以通过value属性指定base resource path。resource的前缀支持”classpath:”和“file:”。如果没有指定前缀,默认认为是文件系统资源。

@ContextConfiguration
@WebAppConfiguration("classpath:test-web-resources")
public class WebAppTests {
// class body...
}


注意,@WebAppConfiguration必须和@ContextConfiguration一起使用,不管是在单个测试类或者是在一个测试类层级中。可以通过阅读@WebAppConfiguration的javadoc进一步了解。

@ContextHierarchy

类级别注解,为集成测试定义ApplicationContexts 的层级结构。@ContextHierarchy应该在一个或多个@ContextConfiguration注释的实例中声明,每一次声明都在context层级中定义一级。下面的例子展示了在单个测试类中的使用,当然,@ContextHierarchy也可以应用于测试类层级中。

@ContextHierarchy({
@ContextConfiguration("/parent-config.xml"),
@ContextConfiguration("/child-config.xml")
})
public class ContextHierarchyTests {
// class body...
}


@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = AppConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
public class WebIntegrationTests {
// class body...
}


如果想在一个测试类层级中合并或者覆盖指定context层级,必须再@ContextConfiguration的name属性中明确的指定对应的层级名称。通过“Context hierarchies” 和@ContextHierarchy 的javadoc获得更多的实例。

@ActiveProfiles

类级别注解,用于定义bean可用的profile。当加载ApplicationContext 时,通过profile来决定此bean时候加载。

@ContextConfiguration
@ActiveProfiles("dev")
public class DeveloperTests {
// class body...
}


@ContextConfiguration
@ActiveProfiles({"dev", "integration"})
public class DeveloperIntegrationTests {
// class body...
}


注意,默认情况下,@ActiveProfiles提供了对于superclass声明的profile信息的继承。可以通过实现自定义ActiveProfilesResolver 解决以编程方式定义bean的profile。需要通过@ActiveProfiles的resolver属性注册ActiveProfilesResolver。

可以通过阅读“Context configuration with environment profiles”和javadoc获得更多信息。

@TestPropertySource

类级别注解,用来配置properties文件的地址和内置属性。TestPropertySource和操作系统环境变量以及Java系统属性,@PropertySource指定,编程指定相比,具有更高的优先性。因此,可以用来选择性覆盖系统或应用程序的属性定义。进一步来说,内置属性和通过resource加载的属性相比,具有更高优先性。

下面的实例在classpath下声明属性文件。

@ContextConfiguration
@TestPropertySource("/test.properties")
public class MyIntegrationTests {
// class body...
}


下面的实例采用内置属性声明方式:

@ContextConfiguration
@TestPropertySource(properties = { "timezone = GMT", "port: 4242" })
public class MyIntegrationTests {
// class body...
}


@DirtiesContext

表名在一个测试的执行中,Spring的ApplicationContext被污染了(被修改,或者被毁坏,比如修改了bean的状态),应该被close。当一个应用程序context标记为dirty,他将会从测试框架中移除和关闭。因此,会为之后的任何一个test重建具有相同元属性的Spring 容器。

在一个类,或者类层次中,@DirtiesContext可以用作类级别,也可以用用在方法级别。因此ApplicationContext 极可能在注解的方法前后被标记为dirty,也可能在注解的类前面或后面标记为dirty。

下面为不同的场景提供了实例。

- 在当前测试类前,在类上声明,并将classMode赋值BEFORE_CLASS

@DirtiesContext(classMode = BEFORE_CLASS)
public class FreshContextTests {
// some tests that require a new Spring container
}


在当前测试类之后

@DirtiesContext
public class ContextDirtyingTests {
// some tests that result in the Spring container being dirtied
}


在当前测试类的每个方法之前

@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD)
public class FreshContextTests {
// some tests that require a new Spring container
}


在当前测试类没法方法之后

@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
public class ContextDirtyingTests {
// some tests that result in the Spring container being dirtied
}


在当前测试方法之前

@DirtiesContext(methodMode = BEFORE_METHOD)
@Test
public void testProcessWhichRequiresFreshAppCtx() {
// some logic that requires a new Spring container
}


在当前测试方法之后

@DirtiesContext
@Test
public void testProcessWhichDirtiesAppCtx() {
// some logic that results in the Spring container being dirtied
}


如果@DirtiesContext在@ContextHierarchy 定义的context层级中使用,hierarchyMode可以用来控制context的清理工作。默认情况下,使用exhaustive 机制,清除当前层级的context以及所有以此context为祖先的所有context。如果exhaustive 机制对于个别场景来说消耗太大,那么可以使用current level机制代替。

@ContextHierarchy({
@ContextConfiguration("/parent-config.xml"),
@ContextConfiguration("/child-config.xml")
})
public class BaseTests {
// class body...
}

public class ExtendedTests extends BaseTests {

@Test
@DirtiesContext(hierarchyMode = CURRENT_LEVEL)
public void test() {
// some logic that results in the child context being dirtied
}
}


可以通过DirtiesContext.HierarchyMode的javadoc获得更详细的介绍。

@TestExecutionListeners

类级别注解,配置TestContextManager中注册的TestExecutionListener 。一般需要和@ContextConfiguration一起使用。

@ContextConfiguration
@TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class})
public class CustomTestExecutionListenerTests {
// class body...
}


默认情况下@TestExecutionListeners支持继承。可以通过阅读javadoc获得更多信息。

@Commit

一个使用事物的测试,在完成测试代码的时候,提交事物。为了让代码的语义更加明确,@Commit可以替代@Rollback(false)。和@Rollback类似,@Commit可以用于类和方法。

@Commit
@Test
public void testProcessWithoutRollback() {
// ...
}


@Rollback

表名在测试代码完成时,是否回滚事物。如果设成true,事物将被回滚;否则事物会被提交。TestContext框架中,@Rollback默认设置成true。如果@Rollback没有显式声明,默认也是回滚。

当作为类级别的注解,@Rollback定义了测试类中所有的测试用例行为。当被定义为方法级别,只作用于当前方法,且覆盖了类级别的@Rollback和@Commit定义。

@Rollback(false)
@Test
public void testProcessWithoutRollback() {
// ...
}


@BeforeTransaction

支持返回void的方法上使用。在测试方法的事物开始之前执行。从Spring Framework 4.3开始,方法不需要必须是public,也可以使用java8 接口的default方法。

@BeforeTransaction
void beforeTransaction() {
// logic to be executed before a transaction is started
}


@AfterTransaction

使用于方法上,在事物结束之后执行。从Spring Framework 4.3开始,方法不需要必须是public,也可以使用java8 接口的default方法。

@AfterTransaction
void afterTransaction() {
// logic to be executed after a transaction has ended
}


@Sql

作用于测试类或测试方法,在集成测试期间,需要在数据库上执行的sql。

@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"})
public void userTest {
// execute code that relies on the test schema and test data
}


可以通过the section called “Executing SQL scripts declaratively with @Sql”获得详细信息。

@SqlConfig

定义怎样解析和执行@sql注解配置的sql。

@Test
@Sql(
scripts = "/test-user-data.sql",
config = @SqlConfig(commentPrefix = "`", separator = "@@")
)
public void userTest {
// execute code that relies on the test data
}


@SqlGroup

注解的容器,集合多个@sql。@SqlGroup可以独立使用,声明多个@sql;也可以和java8的可重复注解结合使用。在java8中,@Sql可以在同一个类或方法上使用多次,生成此注解容器管理。

@Test
@SqlGroup({
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
@Sql("/test-user-data.sql")
)}
public void userTest {
// execute code that uses the test schema and test data
}


15.4.2 标准注解支持

Spring TestContext框架支持下面的标准注解列表。需要注意的是,下面的注解不仅仅用于测试,在Spring Framework中随处可用。

- @Autowired

- @Qualifier

- @Resource (javax.annotation) if JSR-250 is present

- @ManagedBean (javax.annotation) if JSR-250 is present

- @Inject (javax.inject) if JSR-330 is present

- @Named (javax.inject) if JSR-330 is present

- @PersistenceContext (javax.persistence) if JPA is present

- @PersistenceUnit (javax.persistence) if JPA is present

- @Required

- @Transactional

注意,在Spring TestContext框架中,可以使用@PostConstruct和@PreDestroy,但是在实际的使用中,这两个生命周期相关的注解是收到限制的。如果测试类中的一个方法配置了@PostConstruct,这个方法会在所有的@Before函数之前运行,且每个测试用例之前都会运行。另外,如果测试类中的一个方法配置了@PreDestroy,这个方法不会被执行。在测试类中,推荐使用TestContext框架中的生命周期注解。

15.4.3 Spring Junit4测试注解

下面的注解只能和SpringRunner, Spring’s JUnit rules, 或 Spring’s JUnit 4 support classes结合使用。

@IfProfileValue

指定特定的测试环境才启动这些测试用例。如果配置的ProfileValueSource 返回匹配的name-value信息,测试用例才会可用。否则,测试用例将会被忽略,不执行。

@IfProfileValue可以配置在类级别,方法级别,或者两者都配置。类级别的配置会优先于方法级别的配置,这对于当前类的方法和继承的方法都有效。一个测试方法只有在类级别的配置和方法级别的配置。如果没有配置此注解,则说明一直需要执行。此注解类似于JUnit 4的@Ignore,只是Ignore是测试用例一直无效。

@IfProfileValue(name="java.vendor", value="Oracle Corporation")
@Test
public void testProcessWhichRunsOnlyOnOracleJvm() {
// some logic that should run only on Java VMs from Oracle Corporation
}


可以在@IfProfileValue中为value设置多个值(或关系),达到类似于TestNG的测试组功能。

@IfProfileValue(name="test-groups", values={"unit-tests", "integration-tests"})
@Test
public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {
// some logic that should run only for unit and integration test groups
}


@ProfileValueSourceConfiguration

类级别注解,在接到@IfProfileValue配置的Profile值的时候,指定ProfileValueSource 的类型。如果没有配置此注解,则会使用默认的SystemProfileValueSource 。

@ProfileValueSourceConfiguration(CustomProfileValueSource.class)
public class CustomProfileValueSourceTests {
// class body...
}


@Timed

测试用例必须在指定时间段内完成(单位milliseconds)。如果超过时间,则认为测试用例失败。

时间段包括测试方法自身的执行时间,test的重复执行,以及测试固定组件的搭建和销毁。

@Timed(millis=1000)
public void testProcessWithOneSecondTimeout() {
// some logic that should not take longer than 1 second to execute
}


Spring的@Timed和JUnit 4的@Test(timeout=…​)有不同的语法支持。@Test(timeout=…​)会在超时的时候立即结束测试,而Spring的@Timed会等待任务执行结束,再标记任务是失败状态。

@Repeat

测试方法会被重复执行。重复的次数在注解中配置。

重复执行的部分包括测试用例自身,以及测试固定组件的创建和销毁。

@Repeat(10)
@Test
public void testProcessRepeatedly() {
// ...
}


15.4.4 Testing的元注解支持

大部分的测试相关注解都可以用作元注解,来支持使用组合注解定义自定义注解,这样可以简化配置。

TestContext框架中可以作为元注解的注解列表:

- @BootstrapWith

- @ContextConfiguration

- @ContextHierarchy

- @ActiveProfiles

- @TestPropertySource

- @DirtiesContext

- @WebAppConfiguration

- @TestExecutionListeners

- @Transactional

- @BeforeTransaction

- @AfterTransaction

- @Commit

- @Rollback

- @Sql

- @SqlConfig

- @SqlGroup

- @Repeat

- @Timed

- @IfProfileValue

- @ProfileValueSourceConfiguration

实例:

@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class OrderRepositoryTests { }

@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class UserRepositoryTests { }


声明组合注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTest { }


可以使用上面定义的@TransactionalDevTest来配置。

@RunWith(SpringRunner.class)
@TransactionalDevTest
public class OrderRepositoryTests { }

@RunWith(SpringRunner.class)
@TransactionalDevTest
public class UserRepositoryTests { }


可以在Spring Annotation Programming Model获得详细介绍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息