【Spring】Spring Framework Reference Documentation中文版12

Part IV. Testing

测试

 

The adoption of the test-driven-development (TDD) approach to software development is certainly advocated by the Spring team, and so coverage of Springs support for integration testing is covered (alongside best practices for unit testing). The Spring team has found that the correct use of IoC certainly does make both unit and integration testing easier (in that the presence of setter methods and appropriate constructors on classes makes them easier to wire together in a test without having to set up service locator registries and suchlike)? the chapter dedicated solely to testing will hopefully convince you of this as well.

采用测试驱动开发的方法对于软件开发来说是被spring的小组所提倡的并且spring支持的覆盖范围对于集成测试是覆盖的(并且配合单元测试)。spring小组发现正确的使用IOC可以使得单元和集成测试更加简单(在set方法和适当的类的构造器使得他们更加简单对于在测试中并且不需要设置为服务定位注册和诸如此类)?本章节专注于测试将希望可以在测试方面帮到你。

 

13. Introduction to Spring Testing

介绍spring的测试

 

Testing is an integral part of enterprise software development. This chapter focuses on the value-add of the IoC principle to unit testing and on the benefits of the Spring Frameworks support for integration testing. (A thorough treatment of testing in the enterprise is beyond the scope of this reference manual.)

测试是一个不可分割的一部分对于企业应用开发。本章节关注于ioc原则的升值对单元测试和spring框架支持集成测试的益处。(尽管对于测试在企业中取决于相应的参考手册。)

 

14. Unit Testing

单元测试

 

Dependency Injection should make your code less dependent on the container than it would be with traditional Java EE development. The POJOs that make up your application should be testable in JUnit or TestNG tests, with objects simply instantiated using the new operator, without Spring or any other container. You can use mock objects (in conjunction with other valuable testing techniques) to test your code in isolation. If you follow the architecture recommendations for Spring, the resulting clean layering and componentization of your codebase will facilitate easier unit testing. For example, you can test service layer objects by stubbing or mocking DAO or Repository interfaces, without needing to access persistent data while running unit tests.

依赖注入应当使得你的代码可以很少依赖于容器并且是传统的JavaEE开发。POJO使得你的应用可以在JUnitTestNG中进行测试,并且使用新的操作器实现object的实例化,不需要spring和其他容器。你可以使用模拟object(和其他有价值的测试技术一起使用)来独立测试你的代码。如果你遵循spring的构建建议,将使得你的代码整洁并且组件化你的代码库将更加容易被测试。例如,你可以测试服务层通过存储和模拟DAORepository接口,不需要访问不可变的数据当你运行单元测试时。

 

True unit tests typically run extremely quickly, as there is no runtime infrastructure to set up. Emphasizing true unit tests as part of your development methodology will boost your productivity. You may not need this section of the testing chapter to help you write effective unit tests for your IoC-based applications. For certain unit testing scenarios, however, the Spring Framework provides the following mock objects and testing support classes.

真正的单元测试通常运行很快,并且没有运行时设置需要被配置。强调真正的单元测试作为你开发技术的一部分将宣扬你的高产。你或许不需要这一节测试的章节来帮助你写有效的单元测试为了你基于IOC的应用。对于确定的单元测试场景,然而,spring框架提供了如下的模拟object和测试支持类。

 

14.1 Mock Objects

模拟object

 

14.1.1 Environment

环境

 

The org.springframework.mock.env package contains mock implementations of the Environment and PropertySource abstractions (see Section 7.13.1, Bean definition profilesand Section 7.13.3, PropertySource abstraction). MockEnvironment and MockPropertySource are useful for developing out-of-container tests for code that depends on environment-specific properties.

org.springframework.mock.env包中包含了模拟实现环境和PropertySource的抽象(见7.13.1章节“bean定义的profiles”和7.13.3章节“PropertySource抽象”)。MockEnvironmentMockPropertySource是有用的对于开发容器外的测试代码依赖于特定环境属性。

 

14.1.2 JNDI

 

The org.springframework.mock.jndi package contains an implementation of the JNDI SPI, which you can use to set up a simple JNDI environment for test suites or stand-alone applications. If, for example, JDBC DataSources get bound to the same JNDI names in test code as within a Java EE container, you can reuse both application code and configuration in testing scenarios without modification.

org.springframework.mock.jndi包中包含了一个JNDISPI的实现,你可以用于设置一个简单的JNDI环境用于测试或独立的应用。如果例如,JDBC数据源有相同的JNDI名在测试代码中和JavaEE容器相互配合,你可以重用应用代码并且在测试环境中进行配置而不需要修改什么。

 

14.1.3 Servlet API

 

The org.springframework.mock.web package contains a comprehensive set of Servlet API mock objects, which are useful for testing web contexts, controllers, and filters. These mock objects are targeted at usage with Springs Web MVC framework and are generally more convenient to use than dynamic mock objects such as EasyMock or alternative Servlet API mock objects such as MockObjects. Since Spring Framework 4.0, the set of mocks in the org.springframework.mock.web package is based on the Servlet 3.0 API.

org.springframework.mock.web包中包含一个复杂的集合有关servletapi模拟object,可以有益于测试web上下文、容器、过滤器。这些模拟object目标用于springweb mvc框架并且可以比动态模拟object更有用作为一个简单模拟或替代ServletAPI模拟object,例如MockObject。自从spring框架4.0开始,mock的集合在org.springframework.mock.web包中是基于servlet3.0api的。

 

For thorough integration testing of your Spring MVC and REST Controllers in conjunction with your WebApplicationContext configuration for Spring MVC, see the Spring MVC Test Framework.

对于springmvc的测试和REST控制器在你的应用中和WebApplicationContext配置在一起为了springmvc等信息,见springmvc的测试框架。

 

14.1.4 Portlet API

 

The org.springframework.mock.web.portlet package contains a set of Portlet API mock objects, targeted at usage with Springs Portlet MVC framework.

org.springframework.mock.web.portlet包中包含了PortletAPI模拟object的集合,目标使用在springPortletmvc框架。

 

14.2 Unit Testing support Classes

单元测试支持类

 

14.2.1 General testing utilities

通用的测试工具

 

The org.springframework.test.util package contains several general purpose utilities for use in unit and integration testing.

org.springframework.test.util包中包含了一些通用的目标工具用于测试和集成测试。

 

ReflectionTestUtils is a collection of reflection-based utility methods. Developers use these methods in testing scenarios where they need to change the value of a constant, set a non-public field, invoke a non-public setter method, or invoke a non-public configuration or lifecycle callback method when testing application code involving use cases such as the following.

ReflectionTestUtils是一个基于反射的工具方法集合。开发者使用这些方法在测试场景当他们需要常量的值,设置一个非公共的域、调用一个非公共的set方法或调用一个非公共配置或生命周期回调方法当测试应用工代码调用用例如下:

 

    ORM frameworks such as JPA and Hibernate that condone private or protected field access as opposed to public setter methods for properties in a domain entity.

ORM框架例如JPAHibernate允许私有或保护的域访问作为目标对于公共的set方法来设置属性在一个实体中。

    Springs support for annotations such as @Autowired, @Inject, and @Resource, which provides dependency injection for private or protected fields, setter methods, and configuration methods.

spring支持为了注解例如@Autowired@Inject@Resource提供了依赖注入用于私有或保护的域、set方法和配置方法。

    Use of annotations such as @PostConstruct and @PreDestroy for lifecycle callback methods.

使用注解例如@PostConstruct@PreDestroy用于生命周期的回调方法。

 

AopTestUtils is a collection of AOP-related utility methods. These methods can be used to obtain a reference to the underlying target object hidden behind one or more Spring proxies. For example, if you have configured a bean as a dynamic mock using a library like EasyMock or Mockito and the mock is wrapped in a Spring proxy, you may need direct access to the underlying mock in order to configure expectations on it and perform verifications. For Springs core AOP utilities, see AopUtils and AopProxyUtils.

AopTestUtils是一个相关AOP的工具方法集合。这些方法可以被是使用获得一个引用关于目标object在一个或多个spring代理之后。例如,如果你已经配置了一个bean作为一个动态模拟使用一个库像EasyMock和模拟被spring的代理处理,你或许需要直接方法模拟用于配置和执行校验。对于spring的核心aop工具,见AopUtilsAopProxyUtils

 

14.2.2 Spring MVC

 

The org.springframework.test.web package contains ModelAndViewAssert, which you can use in combination with JUnit, TestNG, or any other testing framework for unit tests dealing with Spring MVC ModelAndView objects.

org.springframework.test.web包中包含了ModelAndViewAssert,你可以和JUnitTestNG或其他测试框架一起使用用于单元测试处理SpringMVCModelAndViewobject

 

[Tip]

提示

 

To unit test your Spring MVC Controllers as POJOs, use ModelAndViewAssert combined with MockHttpServletRequest, MockHttpSession, and so on from Springs Servlet API mocks. For thorough integration testing of your Spring MVC and REST Controllers in conjunction with your WebApplicationContext configuration for Spring MVC, use the Spring MVC Test Framework instead.

对于你的springmvc控制器的测试作为pojo,使用ModelAndViewAssertMockHttpServletRequestMockHttpSession等等有关springservletapi模拟。对于集成测试你的springmvcREST控制器配合WebApplicationContext配置为了springmvc,使用springmvc的测试框架作为替代。

 

15. Integration Testing

集成测试

 

15.1 Overview

概览

 

It is important to be able to perform some integration testing without requiring deployment to your application server or connecting to other enterprise infrastructure. This will enable you to test things such as:

这是重要的执行集成测试不需要部署你的应用到应用服务器或连接其他企业环境。这将使得你测试如下:

 

    The correct wiring of your Spring IoC container contexts.

正确的使用了springioc容器上下文。

    Data access using JDBC or an ORM tool. This would include such things as the correctness of SQL statements, Hibernate queries, JPA entity mappings, etc.

使用JDBC或一个ORM工具的数据访问。包括例如正确的sql语句、Hibernate的查询、JPA的实体匹配等。

 

The Spring Framework provides first-class support for integration testing in the spring-test module. The name of the actual JAR file might include the release version and might also be in the long org.springframework.test form, depending on where you get it from (see the section on Dependency Management for an explanation). This library includes the org.springframework.test package, which contains valuable classes for integration testing with a Spring container. This testing does not rely on an application server or other deployment environment. Such tests are slower to run than unit tests but much faster than the equivalent Selenium tests or remote tests that rely on deployment to an application server.

spring框架提供了优秀的支持对于集成测试在spring-test模块。实际的JAR的名字中包括发行的版本号并且以long org.springframework.test的形式(见章节有关独立管理的说明)。库包含了org.springframework.test包,包含了需要有价值的类用于集成测试配合spring的容器。这些测试不会依赖于一个应用服务器或其他开发环境。这样的测试是缓慢的运行比单元测试慢但是比相同测试或远程测试依赖于应用服务器的要快。

 

In Spring 2.5 and later, unit and integration testing support is provided in the form of the annotation-driven Spring TestContext Framework. The TestContext framework is agnostic of the actual testing framework in use, thus allowing instrumentation of tests in various environments including JUnit, TestNG, and so on.

spring2.5及以后,单元测试和集成测试支持提供基于注解的spring的测试上下文框架。测试上下文框架在实际测试框架中被使用,允许测试不同的环境配合JUnitTestNG等等。

 

15.2 Goals of Integration Testing

集成测试的目标

 

Springs integration testing support has the following primary goals:

spring的集成测试支持有以下的主要原则:

 

    To manage Spring IoC container caching between test execution.

为了管理springioc容器缓存在测试执行之间。

    To provide Dependency Injection of test fixture instances.

为了提供依赖注入有关测试工具实例。

    To provide transaction management appropriate to integration testing.

为了提供事务管理使用于集成测试。

    To supply Spring-specific base classes that assist developers in writing integration tests.

为了支持特定的spring的基类断言开发者在处理集成测试中。

 

The next few sections describe each goal and provide links to implementation and configuration details.

下面几节将描述每个目标并联系实现和配置的细节。

 

15.2.1 Context management and caching

上下文管理和缓存

 

The Spring TestContext Framework provides consistent loading of Spring ApplicationContexts and WebApplicationContexts as well as caching of those contexts. Support for the caching of loaded contexts is important, because startup time can become an issue — not because of the overhead of Spring itself, but because the objects instantiated by the Spring container take time to instantiate. For example, a project with 50 to 100 Hibernate mapping files might take 10 to 20 seconds to load the mapping files, and incurring that cost before running every test in every test fixture leads to slower overall test runs that reduce developer productivity.

spring测试框架提供了一致的springApplicationContextsWebApplicationContexts的加载用于缓存上下文。支持缓存加载上下文是重要的,因为启动时间是一个问题————不是spring本身的总开销,但是因为实例化的object通过spring容器需要时间实例化。例如,一个工程有50100Hibernate的映射文件将需要1020秒来加载映射文件,包括启动每个测试的时间在每个测试工具来减少测试运行减少开发者的耗费。

 

Test classes typically declare either an array of resource locations for XML or Groovy configuration metadata — often in the classpath — or an array of annotated classes that is used to configure the application. These locations or classes are the same as or similar to those specified in web.xml or other configuration files for production deployments.

测试类通常定义了资源定位的数组为了xmlGroovy配置元数据————通常在classpath中————或一个声明类的数组被用于配置应用。这些位置或类是相同或相似的对于定义在web.xml中或其他配置文件为了生产开发。

 

By default, once loaded, the configured ApplicationContext is reused for each test. Thus the setup cost is incurred only once per test suite, and subsequent test execution is much faster. In this context, the term test suite means all tests run in the same JVM — for example, all tests run from an Ant, Maven, or Gradle build for a given project or module. In the unlikely case that a test corrupts the application context and requires reloading — for example, by modifying a bean definition or the state of an application object — the TestContext framework can be configured to reload the configuration and rebuild the application context before executing the next test.

默认一次加载配置的ApplicationContext可以在每个测试中重用。启动花销会对于每个测试用例一次并且随后的测试执行会很快。在这个上下文,小组测试意味着所有的测试在相同的JVM上运行————例如所有的测试在AntMaveGradle中执行对于给定的工程或模块。不像测试应用上下文和需要重新加载————例如通过修改bean定义或应用object的状态————测试上下文框架可以被配置来重新加载配置和重新构建应用上下文在执行下一个测试之前。

 

See Section 15.5.4, Context managementand the section called Context cachingwith the TestContext framework.

15.5.4章节“上下文管理”和章节“上下文缓存”有关测试上下文框架。

 

15.2.2 Dependency Injection of test fixtures

测试工具的依赖注入

 

When the TestContext framework loads your application context, it can optionally configure instances of your test classes via Dependency Injection. This provides a convenient mechanism for setting up test fixtures using preconfigured beans from your application context. A strong benefit here is that you can reuse application contexts across various testing scenarios (e.g., for configuring Spring-managed object graphs, transactional proxies, DataSources, etc.), thus avoiding the need to duplicate complex test fixture setup for individual test cases.

当测试框架加载你的应用上下文,可以选择配置实例为你的测试类通过依赖注入。这提供了一个方便的策略为了设置测试工具使用之前在应用上下文配置好的bean。一个好处是你可以拒绝应用上下文跨越不同的测试环境(例如,对于配置spring管理的object graphs、事务代理、数据源等),避免重复复杂的测试工具设置独立的测试用例。

 

As an example, consider the scenario where we have a class, HibernateTitleRepository, that implements data access logic for a Title domain entity. We want to write integration tests that test the following areas:

作为一个例子,考虑一个环境你有一个类HibernateTitleRepository实现了数据访问逻辑对于Title的实体。我们希望编写集成测试和测试如下:

 

    The Spring configuration: basically, is everything related to the configuration of the HibernateTitleRepository bean correct and present?

spring配置:基本是所有相关的配置有关HibernateTitleRepositorybean的正确和表达?

    The Hibernate mapping file configuration: is everything mapped correctly, and are the correct lazy-loading settings in place?

Hibernate映射文件配置:所有配置正确并且正确的延迟加载设置在相应的位置?

    The logic of the HibernateTitleRepository: does the configured instance of this class perform as anticipated?

HibernateTitleRepository的逻辑:确实配置的类的实例来符合预期?

 

See dependency injection of test fixtures with the TestContext framework.

见依赖注入有关测试工具配合测试上下文框架。

 

15.2.3 Transaction management

业务管理

 

One common issue in tests that access a real database is their effect on the state of the persistence store. Even when youre using a development database, changes to the state may affect future tests. Also, many operations??such as inserting or modifying persistent data??cannot be performed (or verified) outside a transaction.

一个通用的测试问题访问一个真实的数据库有关持久化存储的效果。甚至当你使用一个开发数据库,改变其状态或许会影响未来的测试,许多操作例如新增或修改持久化的数据——无法在事务之外实现或验证。

 

The TestContext framework addresses this issue. By default, the framework will create and roll back a transaction for each test. You simply write code that can assume the existence of a transaction. If you call transactionally proxied objects in your tests, they will behave correctly, according to their configured transactional semantics. In addition, if a test method deletes the contents of selected tables while running within the transaction managed for the test, the transaction will roll back by default, and the database will return to its state prior to execution of the test. Transactional support is provided to a test via a PlatformTransactionManager bean defined in the tests application context.

TestContext框架处理了这个问题。默认框架将创建并回滚对于每个测试的事务。你可以简单写代码假设事务的存在。如果你调用业务代理object在你的测试中,他们将正确运行依据他们配置交易流程。此外如果一个测试方法在运行业务管理时删除了选中表的内容用于测试,事务在默认情况下会回滚并且数据库将返回到测试运行之前的状态。事务支持提供测试通过PlatformTransactionManagerbean定义在测试的应用上下文中。

 

If you want a transaction to commit??unusual, but occasionally useful when you want a particular test to populate or modify the database??the TestContext framework can be instructed to cause the transaction to commit instead of roll back via the @Commit annotation.

如果你希望事务被提交————当你希望一个特殊的测试可以新增或修改数据库————TestContext框架可以被要求引起事务的提交代替回滚通过@Commit注解。

 

See transaction management with the TestContext framework.

参考使用TestContext框架的事务管理。

 

15.2.4 Support classes for integration testing

支持集成测试的类

 

The Spring TestContext Framework provides several abstract support classes that simplify the writing of integration tests. These base test classes provide well-defined hooks into the testing framework as well as convenient instance variables and methods, which enable you to access:

SpringTestContext框架提供一些抽象的支持类简单书写了集成测试。这些基本测试类提供了良好的定义在测试框架中作为可供使用的方便变量和方法允许你来使用:

 

    The ApplicationContext, for performing explicit bean lookups or testing the state of the context as a whole.

总的来说ApplicationContext用于实现明确的bean查找或测试上下文的状态。

    A JdbcTemplate, for executing SQL statements to query the database. Such queries can be used to confirm database state both prior to and after execution of database-related application code, and Spring ensures that such queries run in the scope of the same transaction as the application code. When used in conjunction with an ORM tool, be sure to avoid false positives.

JdbcTemplate用于执行SQL语句用于查询数据库。这样的查询可以被使用于确认数据库的状态在执行数据库相关应用代码之前或之后并且spring允许这样的查询被执行在一些事务的范围同应用代码一样。当配合使用ORM工具时,确认避免了误报。

 

In addition, you may want to create your own custom, application-wide superclass with instance variables and methods specific to your project.

此外,你希望创建你自己的应用范围的超类,并且定义了实例变量和方法在你的工程中。

 

See support classes for the TestContext framework.

见用于TestContext框架的支持类说明。

 

15.3 JDBC Testing Support

JDBC测试支持

 

The org.springframework.test.jdbc package contains JdbcTestUtils, which is a collection of JDBC related utility functions intended to simplify standard database testing scenarios. Specifically, JdbcTestUtils provides the following static utility methods.

org.springframework.test.jdbc包包含JdbcTestUtils,是JDBC相关的工具方法集合对于简单的数据库测试场景。特殊的,JdbcTestUtils提供了如下的静态工具方法。

 

    countRowsInTable(..): counts the number of rows in the given table

计算给定表中行的数目

    countRowsInTableWhere(..): counts the number of rows in the given table, using the provided WHERE clause

计算给定表中行的数据,在使用where语句的前提下

    deleteFromTables(..): deletes all rows from the specified tables

删除特定表中所有的数据

    deleteFromTableWhere(..): deletes rows from the given table, using the provided WHERE clause

根据给定的where语句删除给定表中的数据行

    dropTables(..): drops the specified tables

删除特定的表

 

Note that AbstractTransactionalJUnit4SpringContextTests and AbstractTransactionalTestNGSpringContextTests provide convenience methods which delegate to the aforementioned methods in JdbcTestUtils.

注意AbstractTransactionalJUnit4SpringContextTestsAbstractTransactionalTestNGSpringContextTests提供了方法委托给了JdbcTestUtils中的上述方法。

 

The spring-jdbc module provides support for configuring and launching an embedded database which can be used in integration tests that interact with a database. For details, see Section 19.8, Embedded database supportand Section 19.8.5, Testing data access logic with an embedded database.

spring-jdbc模块提供了支持对于配置和运行一个内置数据库被使用在集成测试中并与数据库相互作用。关于细节,将19.8节“内置数据库支持”和19.8.5节“使用嵌入数据库来测试数据访问逻辑”。

 

15.4 Annotations

注解

 

15.4.1 Spring Testing Annotations

spring的测试注解

 

The Spring Framework provides the following set of Spring-specific annotations that you can use in your unit and integration tests in conjunction with the TestContext framework. Refer to the corresponding javadocs for further information, including default attribute values, attribute aliases, and so on.

spring框架提供了下面的特定spring的注解你可以使用在你的单元测试和集成测试中配合你的TestContext框架。参考相关的javadocs来了解更多信息,包括默认属性值、属性别名等等。

 

@BootstrapWith

 

@BootstrapWith is a class-level annotation that is used to configure how the Spring TestContext Framework is bootstrapped. Specifically, @BootstrapWith is used to specify a custom TestContextBootstrapper. Consult the Bootstrapping the TestContext framework section for further details.

@BootstrapWith是一个类级别的注解使用并配置了springTestContext框架是如何引导的,@BootstrapWith被用于定义自定义的TestContextBootstrapper。详见Bootstrapping的相关章节。

 

@ContextConfiguration

 

@ContextConfiguration defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests. Specifically, @ContextConfiguration declares the application context resource locations or the annotated classes that will be used to load the context.

@ContextConfiguration定义了类级别的元数据被用于决定如何加载和配置一个应用上下文用于集成测试。特定的,@ContextConfiguration定义了应用上下文资源位置或注解类将被使用用于加载上下文。

 

Resource locations are typically XML configuration files or Groovy scripts located in the classpath; whereas, annotated classes are typically @Configuration classes. However, resource locations can also refer to files and scripts in the file system, and annotated classes can be component classes, etc.

资源位置通常是xml配置文件或Groovy脚本在classpath中;当注解类被定义了@Configuration类。然而资源位置可以引用在文件系统中的文件和脚本,并且注解类可以是组件类等。

 

@ContextConfiguration("/test-config.xml")

public class XmlApplicationContextTests {

    // class body...

}

 

@ContextConfiguration(classes = TestConfig.class)

public class ConfigClassApplicationContextTests {

    // class body...

}

 

As an alternative or in addition to declaring resource locations or annotated classes, @ContextConfiguration may be used to declare ApplicationContextInitializer classes.

作为选择或定义资源位置或注解类,@ContextConfiguration可以被使用来定义ApplicationContextInitializer类。

 

@ContextConfiguration(initializers = CustomContextIntializer.class)

public class ContextInitializerTests {

    // class body...

}

 

@ContextConfiguration may optionally be used to declare the ContextLoader strategy as well. Note, however, that you typically do not need to explicitly configure the loader since the default loader supports either resource locations or annotated classes as well as initializers.

@ContextConfiguration是可选的被用于定义ContextLoader策略。注意,你通常不需要明确配置加载器因为默认加载器支持资源位置和注解类作为初始化配置。

 

@ContextConfiguration(locations = "/test-context.xml", loader = CustomContextLoader.class)

public class CustomLoaderXmlApplicationContextTests {

    // class body...

}

 

[Note]

注意

 

@ContextConfiguration provides support for inheriting resource locations or configuration classes as well as context initializers declared by superclasses by default.

@ContextConfiguration提供了支持对于资源位置的集成或配置类作为上下文的初始化默认定义在超类中。

 

See Section 15.5.4, Context managementand the @ContextConfiguration javadocs for further details.

15.5.4节“上下文管理”和@ContextConfigurationjavadocs来了解更多信息。

 

@WebAppConfiguration

 

@WebAppConfiguration is a class-level annotation that is used to declare that the ApplicationContext loaded for an integration test should be a WebApplicationContext. The mere presence of @WebAppConfiguration on a test class ensures that a WebApplicationContext will be loaded for the test, using the default value of "file:src/main/webapp" for the path to the root of the web application (i.e., the resource base path). The resource base path is used behind the scenes to create a MockServletContext which serves as the ServletContext for the tests WebApplicationContext.

@WebAppConfiguration是类级别的注解被用于定义继承测试加载的应用上下文的是WebApplicationContext。测试类中@WebAppConfiguration的作用仅仅是保证WebApplicationContext将会被加载用于测试,使用"file:src/main/webapp"的默认值对于web应用的根路径(例如,资源的基准路径)。资源的基准路径用于创建MockServletContext服务于测试的WebApplicationContext作为ServletContext

 

@ContextConfiguration

@WebAppConfiguration

public class WebAppTests {

    // class body...

}

 

To override the default, specify a different base resource path via the implicit value attribute. Both classpath: and file: resource prefixes are supported. If no resource prefix is supplied the path is assumed to be a file system resource.

为了覆盖默认值,定义不同的基准路径通过隐藏的属性。classpathfile资源前缀都是支持的。如果没有资源前缀被提供在路径中将假设是一个文件系统资源。

 

@ContextConfiguration

@WebAppConfiguration("classpath:test-web-resources")

public class WebAppTests {

    // class body...

}

 

Note that @WebAppConfiguration must be used in conjunction with @ContextConfiguration, either within a single test class or within a test class hierarchy. See the @WebAppConfiguration javadocs for further details.

注意@WebAppConfiguration必须配合@ContextConfiguration共同使用,不管是单个测试类还是测试类的结构。详见@WebAppConfigurationjavadocs

 

@ContextHierarchy

 

@ContextHierarchy is a class-level annotation that is used to define a hierarchy of ApplicationContexts for integration tests. @ContextHierarchy should be declared with a list of one or more @ContextConfiguration instances, each of which defines a level in the context hierarchy. The following examples demonstrate the use of @ContextHierarchy within a single test class; however, @ContextHierarchy can also be used within a test class hierarchy.

@ContextHierarchy是一个类级别注解用于定义ApplicationContexts对于集成测试的结构。@ContextHierarchy应当被定义一个或多个@ContextConfiguration实例的列表,每个定义了一个上下文结构的级别。下面的例子展示在单个测试类中使用@ContextHierarchy;然而@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...

}

 

If you need to merge or override the configuration for a given level of the context hierarchy within a test class hierarchy, you must explicitly name that level by supplying the same value to the name attribute in @ContextConfiguration at each corresponding level in the class hierarchy. See the section called Context hierarchiesand the @ContextHierarchy javadocs for further examples.

如果你需要合并或覆盖给定级别上下文结构的配置在一个测试类结构中,你必须定义级别的name支持相同的值对于name属性在@ContextConfiguration在每个相关类级别在类的结构中。详见“上下文结构”和@ContextHierarchyjavadocs

 

@ActiveProfiles

 

@ActiveProfiles is a class-level annotation that is used to declare which bean definition profiles should be active when loading an ApplicationContext for an integration test.

@ActiveProfiles是类级别的注解用于定义bean的定义profile应当被激活当一个应用上下文在集成测试中被加载时。

 

@ContextConfiguration

@ActiveProfiles("dev")

public class DeveloperTests {

    // class body...

}

 

@ContextConfiguration

@ActiveProfiles({"dev", "integration"})

public class DeveloperIntegrationTests {

    // class body...

}

 

[Note]

注意

 

@ActiveProfiles provides support for inheriting active bean definition profiles declared by superclasses by default. It is also possible to resolve active bean definition profiles programmatically by implementing a custom ActiveProfilesResolver and registering it via the resolver attribute of @ActiveProfiles.

@ActiveProfiles提供支持对于继承bean的定义profile默认定义在超类中。也可以编程处理激活bean的定义profile通过实现一个自定义的ActiveProfilesResolver和注册通过@ActiveProfilesresolver属性。

 

See the section called Context configuration with environment profilesand the @ActiveProfiles javadocs for examples and further details.

详见“通过环境profile配置上下文”和@ActiveProfilesjavadocs有关的例子。

 

@TestPropertySource

 

@TestPropertySource is a class-level annotation that is used to configure the locations of properties files and inlined properties to be added to the set of PropertySources in the Environment for an ApplicationContext loaded for an integration test.

@TestPropertySource是一个类级别注解用于配置属性文件的位置和内嵌属性被添加到环境中PropertySource的集合用于集成测试加载ApplicationContext

 

Test property sources have higher precedence than those loaded from the operating systems environment or Java system properties as well as property sources added by the application declaratively via @PropertySource or programmatically. Thus, test property sources can be used to selectively override properties defined in system and application property sources. Furthermore, inlined properties have higher precedence than properties loaded from resource locations.

测试属性代码比从操作系统环境中加载或java系统属性中加载有更高的优先级,和编程添加在应用中通过@PropertySource一样。测试属性代码可以用于选择覆盖属性,被覆盖的属性定义于系统和应用属性代码。更进一步,内嵌的属性有更高优先级比资源路径中加载的属性相比。

 

The following example demonstrates how to declare a properties file from the classpath.

下面的例子展示了如何定义来自classpath的属性文件。

 

@ContextConfiguration

@TestPropertySource("/test.properties")

public class MyIntegrationTests {

    // class body...

}

 

The following example demonstrates how to declare inlined properties.

下面的例子展示了如何定义内嵌的属性。

 

@ContextConfiguration

@TestPropertySource(properties = { "timezone = GMT", "port: 4242" })

public class MyIntegrationTests {

    // class body...

}

 

@DirtiesContext

 

@DirtiesContext indicates that the underlying Spring ApplicationContext has been dirtied during the execution of a test (i.e., modified or corrupted in some manner — for example, by changing the state of a singleton bean) and should be closed. When an application context is marked dirty, it is removed from the testing frameworks cache and closed. As a consequence, the underlying Spring container will be rebuilt for any subsequent test that requires a context with the same configuration metadata.

@DirtiesContext指示了潜在的spring应用上下文被弄脏在测试执行的过程中(例如,修改或损坏在一些情况————例如通过改变单例bean的状态)和应当被关闭的。当应用上下文被标记和脏是,他被测试框架缓存移除并关闭。因此潜在的spring容器将对后面的测试重新构建需要的上下文并且有相同的配置元数据。

 

@DirtiesContext can be used as both a class-level and method-level annotation within the same class or class hierarchy. In such scenarios, the ApplicationContext is marked as dirty before or after any such annotated method as well as before or after the current test class, depending on the configured methodMode and classMode.

@DirtiesContext可以被用于类级别和方法级别注解对于相同的类或类的结构。在这种场景,应用上下文被标记为脏在一些注解方法之前或之后和当前测试类之前或之后,依赖于配置方法模式和类模式。

 

The following examples explain when the context would be dirtied for various configuration scenarios:

下面的例子解释了当上下文是脏的对于不同的配置场景:

 

    Before the current test class, when declared on a class with class mode set to BEFORE_CLASS.

在当前测试类之前,当定义了一个类使用了类模式被设置为BEFORE_CLASS

 

    @DirtiesContext(classMode = BEFORE_CLASS)

    public class FreshContextTests {

        // some tests that require a new Spring container

    }

 

    After the current test class, when declared on a class with class mode set to AFTER_CLASS (i.e., the default class mode).

在当前测试类之后,当定义了一个类使用了类模式被设置为AFTER_CLASS(例如默认的类模式)

 

    @DirtiesContext

    public class ContextDirtyingTests {

        // some tests that result in the Spring container being dirtied

    }

 

    Before each test method in the current test class, when declared on a class with class mode set to BEFORE_EACH_TEST_METHOD.

在每个测试方法之前对于当前测试类,当定义了一个类将类模式设置为BEFORE_EACH_TEST_METHOD

 

    @DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD)

    public class FreshContextTests {

        // some tests that require a new Spring container

    }

 

    After each test method in the current test class, when declared on a class with class mode set to AFTER_EACH_TEST_METHOD.

在每个测试方法之后对于当前测试类,当定义了一个类将类模式设置为AFTER_EACH_TEST_METHOD

 

    @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)

    public class ContextDirtyingTests {

        // some tests that result in the Spring container being dirtied

    }

 

    Before the current test, when declared on a method with the method mode set to BEFORE_METHOD.

在当前测试之前,当定义一个方法将方法模式设置为BEFORE_METHOD

 

    @DirtiesContext(methodMode = BEFORE_METHOD)

    @Test

    public void testProcessWhichRequiresFreshAppCtx() {

        // some logic that requires a new Spring container

    }

 

    After the current test, when declared on a method with the method mode set to AFTER_METHOD (i.e., the default method mode).

在当前测试之后,当定义一个方法将方法模式设置为AFTER_METHOD(例如,默认方法模式)。

 

    @DirtiesContext

    @Test

    public void testProcessWhichDirtiesAppCtx() {

        // some logic that results in the Spring container being dirtied

    }

 

If @DirtiesContext is used in a test whose context is configured as part of a context hierarchy via @ContextHierarchy, the hierarchyMode flag can be used to control how the context cache is cleared. By default an exhaustive algorithm will be used that clears the context cache including not only the current level but also all other context hierarchies that share an ancestor context common to the current test; all ApplicationContexts that reside in a sub-hierarchy of the common ancestor context will be removed from the context cache and closed. If the exhaustive algorithm is overkill for a particular use case, the simpler current level algorithm can be specified instead, as seen below.

如果@DirtiesContext被用于测试,整个测试将被配置作为上下文结构通过@ContextHierarchyhierarchyMode标志位可以用于控制如何上下文缓存被清空。默认下一个详细的算法会被使用清空上下文缓存不只包括当前级别也包括所有其他的上下文结构共享一个祖先上下文对于当前测试,所有应用上下文存在于通用祖先上下文的子结构中将从上下文缓存中被删除和关闭。如果详尽的算法是覆盖特定的用例,简单的当前级别算法可以被定义替换,见如下。

 

@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

    }

}

 

For further details regarding the EXHAUSTIVE and CURRENT_LEVEL algorithms see the DirtiesContext.HierarchyMode javadocs.

对于更多的信息有关EXHAUSTIVECURRENT_LEVEL算法见DirtiesContext.HierarchyModejavadocs

 

@TestExecutionListeners

 

@TestExecutionListeners defines class-level metadata for configuring the TestExecutionListener implementations that should be registered with the TestContextManager. Typically, @TestExecutionListeners is used in conjunction with @ContextConfiguration.

@TestExecutionListeners定义了类级别元数据用于配置TestExecutionListener实现应当被注册通过TestContextManager。通常,@TestExecutionListeners被用于配合@ContextConfiguration注解。

 

@ContextConfiguration

@TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class})

public class CustomTestExecutionListenerTests {

    // class body...

}

 

@TestExecutionListeners supports inherited listeners by default. See the javadocs for an example and further details.

@TestExecutionListeners支持默认继承了监听器。详见javadocs中的例子。

 

@Commit

 

@Commit indicates that the transaction for a transactional test method should be committed after the test method has completed. @Commit can be used as a direct replacement for @Rollback(false) in order to more explicitly convey the intent of the code. Analogous to @Rollback, @Commit may also be declared as a class-level or method-level annotation.

@Commit指示了事务测试方法的事务应当被提交在测试方法完成之后。@Commit可以被用于直接替换对于@Rollback(false)更加明确在代码的意图中。类似于@Rollback@Commit也可以被定义在类级别或方法级别。

 

@Commit

@Test

public void testProcessWithoutRollback() {

    // ...

}

 

@Rollback

 

@Rollback indicates whether the transaction for a transactional test method should be rolled back after the test method has completed. If true, the transaction is rolled back; otherwise, the transaction is committed (see also @Commit). Rollback semantics for integration tests in the Spring TestContext Framework default to true even if @Rollback is not explicitly declared.

@Rollback指示了事务测试方法应当被回滚在测试方法完成之后。如果参数为true,事务被回滚的。此外,事务被提交(将@Commit)。回滚用于集成测试在springTestContext框架默认是true如果@Rollback没有被显示定义。

 

When declared as a class-level annotation, @Rollback defines the default rollback semantics for all test methods within the test class hierarchy. When declared as a method-level annotation, @Rollback defines rollback semantics for the specific test method, potentially overriding class-level @Rollback or @Commit semantics.

当定义类级别的注解,@Rollback定义了默认的回滚语义对于所有的测试方法在测试类的结构中。当定义了一个方法级别的注解,@Rollback定义了对于特定测试方法的回滚语义,潜在的覆盖类级别的@Rollback@Commit语义。

 

@Rollback(false)

@Test

public void testProcessWithoutRollback() {

    // ...

}

 

@BeforeTransaction

 

@BeforeTransaction indicates that the annotated void method should be executed before a transaction is started for test methods configured to run within a transaction via Springs @Transactional annotation. As of Spring Framework 4.3, @BeforeTransaction methods are not required to be public and may be declared on Java 8 based interface default methods.

@BeforeTransaction指示了void方法应当被执行在事务开始之前对于测试方法配置在事务中运行通过spring@Transactional注解。在spring框架4.3中,@BeforeTransaction方法不会被要求是public并且在Java8中定义为接口默认方法。

 

@BeforeTransaction

void beforeTransaction() {

    // logic to be executed before a transaction is started

}

 

@AfterTransaction

 

@AfterTransaction indicates that the annotated void method should be executed after a transaction is ended for test methods configured to run within a transaction via Springs @Transactional annotation. As of Spring Framework 4.3, @AfterTransaction methods are not required to be public and may be declared on Java 8 based interface default methods.

@AfterTransaction修饰void方法应当被执行在配置在事务中的测试方法执行之后通过spring@Transactional注解。在spring框架4.3中,@AfterTransaction方法没有被要求是public并且可以在Java8中定义为接口默认方法。

 

@AfterTransaction

void afterTransaction() {

    // logic to be executed after a transaction has ended

}

 

@Sql

 

@Sql is used to annotate a test class or test method to configure SQL scripts to be executed against a given database during integration tests.

@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

}

 

See the section called Executing SQL scripts declaratively with @Sqlfor further details.

详见章节”执行SQL脚本定义在@Sql“。

 

@SqlConfig

 

@SqlConfig defines metadata that is used to determine how to parse and execute SQL scripts configured via the @Sql annotation.

@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

 

@SqlGroup is a container annotation that aggregates several @Sql annotations. @SqlGroup can be used natively, declaring several nested @Sql annotations, or it can be used in conjunction with Java 8s support for repeatable annotations, where @Sql can simply be declared several times on the same class or method, implicitly generating this container annotation.

@SqlGroup是一个容器注解聚集了一些@Sql注解。@SqlGroup可以在本地使用,定义一些内置的@Sql注解或可以被用于配合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 Standard Annotation Support

标准注解的支持

 

The following annotations are supported with standard semantics for all configurations of the Spring TestContext Framework. Note that these annotations are not specific to tests and can be used anywhere in the Spring Framework.

下面的注解被支持通过标准的语义对于所有springTestContext框架的配置。注意这些注解不是特定针对测试,可以在spring框架的任何位置使用。

 

    @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

 

[Note]

注意

 

In the Spring TestContext Framework @PostConstruct and @PreDestroy may be used with standard semantics on any application components configured in the ApplicationContext; however, these lifecycle annotations have limited usage within an actual test class.

springTestContext框架中@PostConstruct@PreDestroy可以使用标准语义在应用组件配置在ApplicationContext中,然而这些生命周期注解在实际测试类中使用是有限制的。

 

If a method within a test class is annotated with @PostConstruct, that method will be executed before any before methods of the underlying test framework (e.g., methods annotated with JUnit 4s @Before), and that will apply for every test method in the test class. On the other hand, if a method within a test class is annotated with @PreDestroy, that method will never be executed. Within a test class it is therefore recommended to use test lifecycle callbacks from the underlying test framework instead of @PostConstruct and @PreDestroy.

如果一个方法在测试类中被修饰通过@PostConstruct,这个方法将被执行在所有其他spring测试框架的方法之前(例如方法使用了JUnit4@Before注解),并且将被应用用于每个测试方法在测试类。另外如果一个方法有一个测试类被修饰通过@PreDestroy,方法将不会被执行。在一个测试类如果推荐使用测试shengmzq回调从测试框架而不是@PostConstruct@PreDestroy

 

15.4.3 Spring JUnit 4 Testing Annotations

 

The following annotations are only supported when used in conjunction with the SpringRunner, Springs JUnit rules, or Springs JUnit 4 support classes.

下面的注解只支持当配置使用在SpringRunnerspringJUnit规则或springJUnit4支持类。

 

@IfProfileValue

 

@IfProfileValue indicates that the annotated test is enabled for a specific testing environment. If the configured ProfileValueSource returns a matching value for the provided name, the test is enabled. Otherwise, the test will be disabled and effectively ignored.

@IfProfileValue指示了注解测试被启用对于特定的测试环境。如果配置ProfileValueSource返回一个匹配值对于给定的名字,测试被允许。另外,测试将被关闭并且被忽略。

 

@IfProfileValue can be applied at the class level, the method level, or both. Class-level usage of @IfProfileValue takes precedence over method-level usage for any methods within that class or its subclasses. Specifically, a test is enabled if it is enabled both at the class level and at the method level; the absence of @IfProfileValue means the test is implicitly enabled. This is analogous to the semantics of JUnit 4s @Ignore annotation, except that the presence of @Ignore always disables a test.

@IfProfileValue可以被应用在类级别、方法级别或同时。类级别使用@IfProfileValue将替代方法级别对于任何方法定义在类或其子类中。特定的,一个测试被允许如果他同时允许类级别和方法级别,没有@IfProfileValue意味着测试被允许。和JUnit4@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

}

 

Alternatively, you can configure @IfProfileValue with a list of values (with OR semantics) to achieve TestNG-like support for test groups in a JUnit 4 environment. Consider the following example:

作为替代,你可以配置@IfProfileValue在值列别中(配合OR语义)来打包类似TestNG支持在JUnit4环境中的测试组。考虑下面的例子:

 

@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

 

@ProfileValueSourceConfiguration is a class-level annotation that specifies what type of ProfileValueSource to use when retrieving profile values configured through the @IfProfileValue annotation. If @ProfileValueSourceConfiguration is not declared for a test, SystemProfileValueSource is used by default.

@ProfileValueSourceConfiguration是一个类级别的注解定义了ProfileValueSource的类型用于获得profile值配置通过@IfProfileValue注解中。如果@ProfileValueSourceConfiguration没有在测试中被定义,默认将使用SystemProfileValueSource

 

@ProfileValueSourceConfiguration(CustomProfileValueSource.class)

public class CustomProfileValueSourceTests {

    // class body...

}

 

@Timed

 

@Timed indicates that the annotated test method must finish execution in a specified time period (in milliseconds). If the text execution time exceeds the specified time period, the test fails.

@Timed指示注解测试方法必须完成执行在特定的时间段(单位是毫秒)。如果测试执行时间超过的时间限制,测试会失败。

 

The time period includes execution of the test method itself, any repetitions of the test (see @Repeat), as well as any set up or tear down of the test fixture.

时间段指示了方法本身执行的时间,任何测试的重复(将@Repeat)都会记录测试的开始和结束。

 

@Timed(millis=1000)

public void testProcessWithOneSecondTimeout() {

    // some logic that should not take longer than 1 second to execute

}

 

Springs @Timed annotation has different semantics than JUnit 4s @Test(timeout=…​) support. Specifically, due to the manner in which JUnit 4 handles test execution timeouts (that is, by executing the test method in a separate Thread), @Test(timeout=…​) preemptively fails the test if the test takes too long. Springs @Timed, on the other hand, does not preemptively fail the test but rather waits for the test to complete before failing.

spring@Timed注解和JUnit4@Test(timeout=...)支持是有却别的。特定的,对于Junit4处理测试执行的超时的习惯(执行测试方法在一个独立的线程),@Test(timeout=…​)会失败如果测试花费时间过长。spring@Timed从另一方面不会失败直到测试结束之前。

 

@Repeat

 

@Repeat indicates that the annotated test method must be executed repeatedly. The number of times that the test method is to be executed is specified in the annotation.

@Repeat指示了注解的测试方法必须重复执行。测试方法执行的次数被定义在注解中。

 

The scope of execution to be repeated includes execution of the test method itself as well as any set up or tear down of the test fixture.

执行的范围被重复指示测试方法的执行本身包括方法的开始和结束。

 

@Repeat(10)

@Test

public void testProcessRepeatedly() {

    // ...

}

 

15.4.4 Meta-Annotation Support for Testing

对于测试的元注解支持

 

It is possible to use most test-related annotations as meta-annotations in order to create custom composed annotations and reduce configuration duplication across a test suite.

可以使用测试相关的注解作为元注解用于创建自定义的组合注解并减少重复的配置在测试中。

 

Each of the following may be used as meta-annotations in conjunction with the TestContext framework.

下面列出的每一项都可以被用于元注解配合TestContext框架来使用。

 

    @BootstrapWith

    @ContextConfiguration

    @ContextHierarchy

    @ActiveProfiles

    @TestPropertySource

    @DirtiesContext

    @WebAppConfiguration

    @TestExecutionListeners

    @Transactional

    @BeforeTransaction

    @AfterTransaction

    @Commit

    @Rollback

    @Sql

    @SqlConfig

    @SqlGroup

    @Repeat

    @Timed

    @IfProfileValue

    @ProfileValueSourceConfiguration

 

For example, if we discover that we are repeating the following configuration across our JUnit 4 based test suite…​

例如,如果我们发现我们重复了下面的配置通过JUnit4基本测试。

 

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

 

We can reduce the above duplication by introducing a custom composed annotation that centralizes the common test configuration like this:

我们可以减少上面的重复通过引入一个自定义组合注解使得可以集中处理测试配置如下:

 

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})

@ActiveProfiles("dev")

@Transactional

public @interface TransactionalDevTest { }

 

Then we can use our custom @TransactionalDevTest annotation to simplify the configuration of individual test classes as follows:

下面我们可以使用我们自定义@TransactionalDevTest注解来定义和配置独立的测试类如下:

 

@RunWith(SpringRunner.class)

@TransactionalDevTest

public class OrderRepositoryTests { }

 

@RunWith(SpringRunner.class)

@TransactionalDevTest

public class UserRepositoryTests { }

 

For further details, consult the Spring Annotation Programming Model.

更多的信息,见spring注解编程模型。

 

15.5 Spring TestContext Framework

springTestContext框架

 

The Spring TestContext Framework (located in the org.springframework.test.context package) provides generic, annotation-driven unit and integration testing support that is agnostic of the testing framework in use. The TestContext framework also places a great deal of importance on convention over configuration with reasonable defaults that can be overridden through annotation-based configuration.

springTestContext框架(在org.springframework.test.context包中)提供了通用的基于注解单元和集成测试支持对于测试框架的使用不可知。TestContext框架也可以替代一些重要的规定通过合理的配置默认可以被覆盖通过基于注解的配置。

 

In addition to generic testing infrastructure, the TestContext framework provides explicit support for JUnit 4 and TestNG in the form of abstract support classes. For JUnit 4, Spring also provides a custom JUnit Runner and custom JUnit Rules that allow one to write so-called POJO test classes. POJO test classes are not required to extend a particular class hierarchy.

此外对于通用的测试基础,TestContext框架提供了明确的支持对于JUnit4TestNG以抽象支持类的形式。对于JUnit4spring也支持一个自定义JUnit运行和自定义JUnit规则允许可以调用POJO测试类。POJO测试类不需要扩展特定的类结构。

 

The following section provides an overview of the internals of the TestContext framework. If you are only interested in using the framework and not necessarily interested in extending it with your own custom listeners or custom loaders, feel free to go directly to the configuration (context management, dependency injection, transaction management), support classes, and annotation support sections.

下面的章节提供了一个概览有关TestContext框架的内部构件。如果你对使用框架有兴趣并且不关心自定义监听器的扩展或自定义加载器,可以跳过相关内容(上下文管理、依赖注入、事务管理)支持类和注解支持章节。

 

15.5.1 Key abstractions

 

The core of the framework consists of the TestContextManager class and the TestContext, TestExecutionListener, and SmartContextLoader interfaces. A TestContextManager is created per test class (e.g., for the execution of all test methods within a single test class in JUnit 4). The TestContextManager in turn manages a TestContext that holds the context of the current test. The TestContextManager also updates the state of the TestContext as the test progresses and delegates to TestExecutionListener implementations, which instrument the actual test execution by providing dependency injection, managing transactions, and so on. A SmartContextLoader is responsible for loading an ApplicationContext for a given test class. Consult the javadocs and the Spring test suite for further information and examples of various implementations.

框架的核心包括TestContextManager类、TestContextTestExecutionListenerSmartContextLoader接口。一个TestContextManager被创建通过每个测试类(例如对于所有测试方法的执行在JUnit4中的单个测试类)。TestContextManager反过来管理TestContext持有当前测试的上下文。TestContextManager也会更新TestContext的状态作为测试进程并且委托给TestExecutionListener的实现,执行实际的测试通过依赖注入、管理事务等等。SmartContextLoader用于加载一个ApplicationContext对于给定的测试类。查阅javadocsspring的测试包来了解更多的信息和不同的实现案例。

 

TestContext

 

TestContext encapsulates the context in which a test is executed, agnostic of the actual testing framework in use, and provides context management and caching support for the test instance for which it is responsible. The TestContext also delegates to a SmartContextLoader to load an ApplicationContext if requested.

TestContext在测试执行时包含上下文,实际的测试框架不可知并且提供上下文管理和缓存支持对于测试实例并且是职责所在。TestContext也委托给SmartContextLoader来加载ApplicationContext如果需要的话。

 

 

TestContextManager

 

TestContextManager is the main entry point into the Spring TestContext Framework, which manages a single TestContext and signals events to each registered TestExecutionListener at well-defined test execution points:

TestContextManager是主要的入口对于springTestContext框架,管理单个TestContext和关键事件对于每个注册的TestExecutionListener在良好的测试执行点:

 

    prior to any before class or before all methods of a particular testing framework

指向任何类之前或所有方法之前对于一个特定的测试框架

    test instance post-processing

测试实例后处理

    prior to any before or before each methods of a particular testing framework

指向任何方法或任何之前对于一个特定的测试框架

    after any after or after each methods of a particular testing framework

在任何之后或每个方法之后对于一个特定的测试框架

    after any after class or after all methods of a particular testing framework

在每个类或每个方法之后对于一个特定的测试框架

 

 

TestExecutionListener

 

TestExecutionListener defines the API for reacting to test execution events published by the TestContextManager with which the listener is registered. See Section 15.5.3, TestExecutionListener configuration.

TestExecutionListener定义了API对于每个测试执行事件通过TestContextManager来发布通过监听器被注册。见15.5.3节““TestExecutionListener的配置”

 

Context Loaders

 

ContextLoader is a strategy interface that was introduced in Spring 2.5 for loading an ApplicationContext for an integration test managed by the Spring TestContext Framework. Implement SmartContextLoader instead of this interface in order to provide support for annotated classes, active bean definition profiles, test property sources, context hierarchies, and WebApplicationContext support.

ContextLoader是一个策略接口被引用在spring2.5对于加载ApplicationContext对于一个集成测试通过springTestContext框架来管理。实现SmartContextLoader代替了这个接口以便提供支持对于注解类,激活bean定义profile,测试属性source、上下文结构和WebApplicationContext支持。

 

SmartContextLoader is an extension of the ContextLoader interface introduced in Spring 3.1. The SmartContextLoader SPI supersedes the ContextLoader SPI that was introduced in Spring 2.5. Specifically, a SmartContextLoader can choose to process resource locations, annotated classes, or context initializers. Furthermore, a SmartContextLoader can set active bean definition profiles and test property sources in the context that it loads.

SmartContextLoaderContextLoader接口的一个扩展在spring3.1中。SmartContextLoaderSPI接替了ContextLoaderSPI引入在spring2.5中。特定的一个SmartContextLoader可以选择执行资源位置、注解类或上下文初始化。更进一步一个SmartContextLoader可以被设置激活bean定义profile和测试属性source在他们加载的上下文中。

 

Spring provides the following implementations:

spring提供了下面的实现:

 

    DelegatingSmartContextLoader: one of two default loaders which delegates internally to an AnnotationConfigContextLoader, a GenericXmlContextLoader, or a GenericGroovyXmlContextLoader depending either on the configuration declared for the test class or on the presence of default locations or default configuration classes. Groovy support is only enabled if Groovy is on the classpath.

DelegatingSmartContextLoader:两个默认加载器之一代表AnnotationConfigContextLoaderGenericXmlContextLoader或者GenericGroovyXmlContextLoader依赖于测试类的配置和默认配置的位置。支持Groovy如果Groovyclasspath中。

    WebDelegatingSmartContextLoader: one of two default loaders which delegates internally to an AnnotationConfigWebContextLoader, a GenericXmlWebContextLoader, or a GenericGroovyXmlWebContextLoader depending either on the configuration declared for the test class or on the presence of default locations or default configuration classes. A web ContextLoader will only be used if @WebAppConfiguration is present on the test class. Groovy support is only enabled if Groovy is on the classpath.

WebDelegatingSmartContextLoader:两个默认加载器之一代表AnnotationConfigWebContextLoaderGenericXmlWebContextLoaderGenericGroovyXmlWebContextLoader依赖于测试类的配置或默认配置的位置。一个webContextLoader将被使用如果@WebAppConfiguration在测试类中。如果Groovyclasspath中则支持Groovy

    AnnotationConfigContextLoader: loads a standard ApplicationContext from annotated classes.

AnnotationConfigContextLoader:加载一个标准的ApplicationContext从注解类中。

    AnnotationConfigWebContextLoader: loads a WebApplicationContext from annotated classes.

AnnotationConfigWebContextLoader:加载一个WebApplicationContext从注解类中。

    GenericGroovyXmlContextLoader: loads a standard ApplicationContext from resource locations that are either Groovy scripts or XML configuration files.

GenericGroovyXmlContextLoader:加载一个标准的ApplicationContext从资源位置定义在Groovy脚本或xml配置文件中。

    GenericGroovyXmlWebContextLoader: loads a WebApplicationContext from resource locations that are either Groovy scripts or XML configuration files.

GenericGroovyXmlWebContextLoader:加载一个WebApplicationContext从资源位置定义在Groovy脚本或xml配置文件中。

    GenericXmlContextLoader: loads a standard ApplicationContext from XML resource locations.

GenericXmlContextLoader:加载一个标准的ApplicationContextxml资源位置。

    GenericXmlWebContextLoader: loads a WebApplicationContext from XML resource locations.

GenericXmlWebContextLoader:加载一个WebApplicationContextxml资源位置。

    GenericPropertiesContextLoader: loads a standard ApplicationContext from Java Properties files.

GenericPropertiesContextLoader:加载一个标准的ApplicationContext来自Java属性文件。

 

15.5.2 Bootstrapping the TestContext framework

TestContext框架的引导

 

The default configuration for the internals of the Spring TestContext Framework is sufficient for all common use cases. However, there are times when a development team or third party framework would like to change the default ContextLoader, implement a custom TestContext or ContextCache, augment the default sets of ContextCustomizerFactory and TestExecutionListener implementations, etc. For such low level control over how the TestContext framework operates, Spring provides a bootstrapping strategy.

默认配置对于springTestContext框架是足够的对于所有的通用环境。然而,当开发组或第三方框架希望改变默认的ContextLoader,实现一个自定义的TestContextContextCache,更改默认的集合有关ContextCustomizerFactoryTestExecutionListener的实现等等。对于这些低级的控制TestContext框架的操作,spring提供了引导的策略。

 

TestContextBootstrapper defines the SPI for bootstrapping the TestContext framework. A TestContextBootstrapper is used by the TestContextManager to load the TestExecutionListener implementations for the current test and to build the TestContext that it manages. A custom bootstrapping strategy can be configured for a test class (or test class hierarchy) via @BootstrapWith, either directly or as a meta-annotation. If a bootstrapper is not explicitly configured via @BootstrapWith, either the DefaultTestContextBootstrapper or the WebTestContextBootstrapper will be used, depending on the presence of @WebAppConfiguration.

TestContextBootstrapper定义了引导TestContext框架的SPITestContextBootstrapper被用于TestContextManager来加载TestExecutionListener,为了当前的测试来构建他管理的TestContext。一个自定义的引导策略可以被配置为了一个测试类(或测试类的结构)通过@BootstrapWith,或者直接作为一个元注解。如果引导加载器没有通过@BootstrapWith来配置,DefaultTestContextBootstrapperWebTestContextBootstrapper将被使用,依赖于@WebAppConfiguration的处理。

 

Since the TestContextBootstrapper SPI is likely to change in the future in order to accommodate new requirements, implementers are strongly encouraged not to implement this interface directly but rather to extend AbstractTestContextBootstrapper or one of its concrete subclasses instead.

自从TestContextBootstrapperSPI在未来发生改变以适应新的需求,强烈推荐不要直接实现这个接口但是可以继承AbstractTestContextBootstrapper或他的一个子类作为替代。

 

15.5.3 TestExecutionListener configuration

TestExecutionListener的配置

 

Spring provides the following TestExecutionListener implementations that are registered by default, exactly in this order.

spring提供了下列的TestExecutionListener实现并且默认被注册以下面的顺序。

 

    ServletTestExecutionListener: configures Servlet API mocks for a WebApplicationContext

ServletTestExecutionListener:为WebApplicationContext配置ServletAPI模拟

    DirtiesContextBeforeModesTestExecutionListener: handles the @DirtiesContext annotation for before modes

DirtiesContextBeforeModesTestExecutionListener:为之前的模式处理@DirtiesContext注解

    DependencyInjectionTestExecutionListener: provides dependency injection for the test instance

DependencyInjectionTestExecutionListener:为测试实例提供依赖注入

    DirtiesContextTestExecutionListener: handles the @DirtiesContext annotation for after modes

DirtiesContextTestExecutionListener:为后面的模式处理@DirtiesContext注解

    TransactionalTestExecutionListener: provides transactional test execution with default rollback semantics

TransactionalTestExecutionListener:提供业务测试执行对于默认的回滚语义

    SqlScriptsTestExecutionListener: executes SQL scripts configured via the @Sql annotation

SqlScriptsTestExecutionListener:执行SQL脚本通过@Sql注解

 

Registering custom TestExecutionListeners

注册自定义TestExecutionListeners

 

Custom TestExecutionListeners can be registered for a test class and its subclasses via the @TestExecutionListeners annotation. See annotation support and the javadocs for @TestExecutionListeners for details and examples.

自定义TestExecutionListeners可以被注册为了一个测试类和他的子类通过@TestExecutionListeners注解。见注解支持和javadocs@TestExecutionListeners来查看细节和例子。

 

Automatic discovery of default TestExecutionListeners

自动发现默认的TestExecutionListeners

 

Registering custom TestExecutionListeners via @TestExecutionListeners is suitable for custom listeners that are used in limited testing scenarios; however, it can become cumbersome if a custom listener needs to be used across a test suite. Since Spring Framework 4.1, this issue is addressed via support for automatic discovery of default TestExecutionListener implementations via the SpringFactoriesLoader mechanism.

通过@TestExecutionListeners注册自定义TestExecutionListeners可以适合自定义监听器被限制使用在测试场景,然而,他可以是笨重的如果自定义监听器需要被使用跨越测试套件。自从spring框架4.1,这些问题可以被处理通过支持通过SpringFactoriesLoader的策略为了自动发现默认的TestExecutionListeners

 

Specifically, the spring-test module declares all core default TestExecutionListeners under the org.springframework.test.context.TestExecutionListener key in its META-INF/spring.factories properties file. Third-party frameworks and developers can contribute their own TestExecutionListeners to the list of default listeners in the same manner via their own META-INF/spring.factories properties file.

特定的,spring-test模块定义了所有的默认核心TestExecutionListenersorg.springframework.test.context.TestExecutionListener下,配置在META-INF/spring.factories的属性文件中。第三方框架和开发者可以贡献他们自己的TestExecutionListeners对于默认监听器的列表以相同的方式配置在他们自己的META-INF/spring.factories的属性文件中。

 

Ordering TestExecutionListeners

排序TestExecutionListeners

 

When the TestContext framework discovers default TestExecutionListeners via the aforementioned SpringFactoriesLoader mechanism, the instantiated listeners are sorted using Springs AnnotationAwareOrderComparator which honors Springs Ordered interface and @Order annotation for ordering. AbstractTestExecutionListener and all default TestExecutionListeners provided by Spring implement Ordered with appropriate values. Third-party frameworks and developers should therefore make sure that their default TestExecutionListeners are registered in the proper order by implementing Ordered or declaring @Order. Consult the javadocs for the getOrder() methods of the core default TestExecutionListeners for details on what values are assigned to each core listener.

TestContext框架发现默认的TestExecutionListeners通过上面提到的SpringFactoriesLoader策略,实例化监听器被排序通过使用springAnnotationAwareOrderComparator继承springOrdered接口或使用@Order注解修饰。spring提供的AbstractTestExecutionListener和所有默认的TestExecutionListeners实现了Ordered接口并给定了适当的值。第三方框架和开发者应当确认他们默认的TestExecutionListeners被注册在适当的顺序上通过实现Ordered接口或使用@Order注解。查阅javadocs获得getOrder方法有关TestExecutionListeners的细节关于不同核心监听器的值。

 

Merging TestExecutionListeners

合并TestExecutionListeners

 

If a custom TestExecutionListener is registered via @TestExecutionListeners, the default listeners will not be registered. In most common testing scenarios, this effectively forces the developer to manually declare all default listeners in addition to any custom listeners. The following listing demonstrates this style of configuration.

如果一个自定义TestExecutionListener被注册通过@TestExecutionListeners,默认监听器将不会被注册。在相同的测试场景,这些优先的强制的开发者手动定义所有默认的监听器以应对任何自定义监听器。下面的监听器示范了这种风格的配置。

 

@ContextConfiguration

@TestExecutionListeners({

    MyCustomTestExecutionListener.class,

    ServletTestExecutionListener.class,

    DirtiesContextBeforeModesTestExecutionListener.class,

    DependencyInjectionTestExecutionListener.class,

    DirtiesContextTestExecutionListener.class,

    TransactionalTestExecutionListener.class,

    SqlScriptsTestExecutionListener.class

})

public class MyTest {

    // class body...

}

 

The challenge with this approach is that it requires that the developer know exactly which listeners are registered by default. Moreover, the set of default listeners can change from release to release — for example, SqlScriptsTestExecutionListener was introduced in Spring Framework 4.1, and DirtiesContextBeforeModesTestExecutionListener was introduced in Spring Framework 4.2. Furthermore, third-party frameworks like Spring Security register their own default TestExecutionListeners via the aforementioned automatic discovery mechanism.

这种方式的挑战是他需要开发者明确了解哪个监听器被默认注册。而且,默认监听器的集合可以改变从一个发布到另一个发布————例如,SqlScriptsTestExecutionListener被引入在spring框架4.1并且DirtiesContextBeforeModesTestExecutionListener被引入在spring框架4.2。此外第三方框架例如springsecurity注册他们自己的TestExecutionListeners通过上面提到的自动发现策略。

 

To avoid having to be aware of and re-declare all default listeners, the mergeMode attribute of @TestExecutionListeners can be set to MergeMode.MERGE_WITH_DEFAULTS. MERGE_WITH_DEFAULTS indicates that locally declared listeners should be merged with the default listeners. The merging algorithm ensures that duplicates are removed from the list and that the resulting set of merged listeners is sorted according to the semantics of AnnotationAwareOrderComparator as described in the section called Ordering TestExecutionListeners. If a listener implements Ordered or is annotated with @Order it can influence the position in which it is merged with the defaults; otherwise, locally declared listeners will simply be appended to the list of default listeners when merged.

为了避免意识到重定义所有默认的监听器,@TestExecutionListenersmergeMode属性可以被设置为MergeMode.MERGE_WITH_DEFAULTSMergeMode.MERGE_WITH_DEFAULTS指示本地定义的监听器应当被合并和默认的监听器。合并策略允许副本从列表中被移除使得合并监听器的集合被排序依据AnnotationAwareOrderComparator的语义描述在“排序TestExecutionListeners”的章节中。如果一个监听器实现了Ordered接口或使用了@Order注解可以影响他合并默认的位置,此外本地定义监听器将添加到默认将被合并的监听器列表中。

 

For example, if the MyCustomTestExecutionListener class in the previous example configures its order value (for example, 500) to be less than the order of the ServletTestExecutionListener (which happens to be 1000), the MyCustomTestExecutionListener can then be automatically merged with the list of defaults in front of the ServletTestExecutionListener, and the previous example could be replaced with the following.

例如,如果MyCustomTestExecutionListener类在之前的例子中配置他的顺序值(例如,500)比ServletTestExecutionListener的值小(比如是1000),那么MyCustomTestExecutionListener可以自动被合并默认列表在ServletTestExecutionListener之前并且之前的例如可以被替代如下。

 

@ContextConfiguration

@TestExecutionListeners(

    listeners = MyCustomTestExecutionListener.class,

    mergeMode = MERGE_WITH_DEFAULTS

)

public class MyTest {

    // class body...

}

 

15.5.4 Context management

上下文管理

 

Each TestContext provides context management and caching support for the test instance it is responsible for. Test instances do not automatically receive access to the configured ApplicationContext. However, if a test class implements the ApplicationContextAware interface, a reference to the ApplicationContext is supplied to the test instance. Note that AbstractJUnit4SpringContextTests and AbstractTestNGSpringContextTests implement ApplicationContextAware and therefore provide access to the ApplicationContext automatically.

每个TestContext提供了上下文管理和缓存支持对于测试实例。测试实例不会自动收到配置ApplicationContext的接入。然而如果一个测试类实现了ApplicationContextAware接口,一个ApplicationContext的引用会被提供给测试实例。注意AbstractJUnit4SpringContextTestsAbstractTestNGSpringContextTests实现了ApplicationContextAware并自动提供了方法ApplicationContext的支持。

 

[Tip]

提示

 

As an alternative to implementing the ApplicationContextAware interface, you can inject the application context for your test class through the @Autowired annotation on either a field or setter method. For example:

作为实现ApplicationContextAware接口的替代,你可以注入应用上下文为你的测试类通过@Autowired注解修饰属性或set方法。例如:

 

@RunWith(SpringRunner.class)

@ContextConfiguration

public class MyTest {

 

    @Autowired

    private ApplicationContext applicationContext;

 

    // class body...

}

 

Similarly, if your test is configured to load a WebApplicationContext, you can inject the web application context into your test as follows:

相似的,如果你的测试是配置通过加载WebApplicationContext,你可以注入web应用上下文到你的测试中如下:

 

@RunWith(SpringRunner.class)

@WebAppConfiguration

@ContextConfiguration

public class MyWebAppTest {

    @Autowired

    private WebApplicationContext wac;

 

    // class body...

}

 

Dependency injection via @Autowired is provided by the DependencyInjectionTestExecutionListener which is configured by default (see Section 15.5.5, Dependency injection of test fixtures).

依赖注入通过@AutowiredDependencyInjectionTestExecutionListener提供的被默认配置(将15.5.5节,“测试特性的依赖注入”)

 

Test classes that use the TestContext framework do not need to extend any particular class or implement a specific interface to configure their application context. Instead, configuration is achieved simply by declaring the @ContextConfiguration annotation at the class level. If your test class does not explicitly declare application context resource locations or annotated classes, the configured ContextLoader determines how to load a context from a default location or default configuration classes. In addition to context resource locations and annotated classes, an application context can also be configured via application context initializers.

测试类使用了TestContext框架不需要继承任何特定的类或实现一个特定接口来配置他们的应用上下文。作为代替,配置可以简单被定义@ContextConfiguration注解在类的级别。如果你的测试类不需要明确定义应用上下文资源位置或注解类,配置ContextLoader决定了如何加载上下文通过默认的位置或默认的配置类。此外上下文资源位置和注解类,应用上下文可以被配置通过应用上下文初始化。

 

The following sections explain how to configure an ApplicationContext via XML configuration files, Groovy scripts, annotated classes (typically @Configuration classes), or context initializers using Springs @ContextConfiguration annotation. Alternatively, you can implement and configure your own custom SmartContextLoader for advanced use cases.

下面的章节解释了如何配置ApplicationContext通过xml配置文件、Groovy脚本、注解类(通常是@Configuration类)或上下文初始化通过使用spring@ContextConfiguration注解。作为替代,你可以实现和配置你自己的SmartContextLoader对于高级使用案例。

 

Context configuration with XML resources

使用xml资源的上下文配置

 

To load an ApplicationContext for your tests using XML configuration files, annotate your test class with @ContextConfiguration and configure the locations attribute with an array that contains the resource locations of XML configuration metadata. A plain or relative path??for example "context.xml"??will be treated as a classpath resource that is relative to the package in which the test class is defined. A path starting with a slash is treated as an absolute classpath location, for example "/org/example/config.xml". A path which represents a resource URL (i.e., a path prefixed with classpath:, file:, http:, etc.) will be used as is.

为了加载ApplicationContext到你的测试使用xml配置文件,使用@ContextConfiguration来修饰你的类并使用数组配置location属性包含xml配置元数据的资源位置。一个普通或相对的路径————例如“context.xml”————将被认为是classpath资源相对于你定义的类的包。如果路径以斜线开始则视为绝对路径,例如“/org/example/config.xml”。一个路径代表一个资源的URL(例如,一个路径前缀是classpath:http:等等)。

 

@RunWith(SpringRunner.class)

// ApplicationContext will be loaded from "/app-config.xml" and

// "/test-config.xml" in the root of the classpath

@ContextConfiguration(locations={"/app-config.xml", "/test-config.xml"})

public class MyTest {

    // class body...

}

 

@ContextConfiguration supports an alias for the locations attribute through the standard Java value attribute. Thus, if you do not need to declare additional attributes in @ContextConfiguration, you can omit the declaration of the locations attribute name and declare the resource locations by using the shorthand format demonstrated in the following example.

@ContextConfiguration支持别名对于location属性通过共享javavalue属性。因此如果你不需要定义额外的属性在@ContextConfiguration中,你可以省略location属性名和,并使用简写的形式定义资源路径如下。

 

@RunWith(SpringRunner.class)

@ContextConfiguration({"/app-config.xml", "/test-config.xml"})

public class MyTest {

    // class body...

}

 

If you omit both the locations and value attributes from the @ContextConfiguration annotation, the TestContext framework will attempt to detect a default XML resource location. Specifically, GenericXmlContextLoader and GenericXmlWebContextLoader detect a default location based on the name of the test class. If your class is named com.example.MyTest, GenericXmlContextLoader loads your application context from "classpath:com/example/MyTest-context.xml".

如果你同时省略@ContextConfiguration中的locationvalue属性,TestContext框架将试图探测默认的xml资源路径。特定的,GenericXmlContextLoaderGenericXmlWebContextLoader探测默认的位置基于测试类的名字。如果你的类名字为com.example.MyTestGenericXmlContextLoader加载你的应用上下文从"classpath:com/example/MyTest-context.xml"中。

 

package com.example;

 

@RunWith(SpringRunner.class)

// ApplicationContext will be loaded from

// "classpath:com/example/MyTest-context.xml"

@ContextConfiguration

public class MyTest {

    // class body...

}

 

Context configuration with Groovy scripts

使用Groovy脚本实现上下文配置

 

To load an ApplicationContext for your tests using Groovy scripts that utilize the Groovy Bean Definition DSL, annotate your test class with @ContextConfiguration and configure the locations or value attribute with an array that contains the resource locations of Groovy scripts. Resource lookup semantics for Groovy scripts are the same as those described for XML configuration files.

为了使你的测试加载ApplicationContext通过使用Groovy脚本可以利用Groovybean定义DSL,注释你的测试类使用@ContextConfiguration和配置locationvalue属性通过使用一个数组包括Groovy脚本的路径。对于Groovy脚本的资源查找策略和描述xml配置文件是相同的。

 

[Tip]

提示

 

Support for using Groovy scripts to load an ApplicationContext in the Spring TestContext Framework is enabled automatically if Groovy is on the classpath.

支持使用Groovy脚本来加载ApplicationContext,在springTestContext框架允许自动如果Groovyclasspath中。

 

@RunWith(SpringRunner.class)

// ApplicationContext will be loaded from "/AppConfig.groovy" and

// "/TestConfig.groovy" in the root of the classpath

@ContextConfiguration({"/AppConfig.groovy", "/TestConfig.Groovy"})

public class MyTest {

    // class body...

}

 

If you omit both the locations and value attributes from the @ContextConfiguration annotation, the TestContext framework will attempt to detect a default Groovy script. Specifically, GenericGroovyXmlContextLoader and GenericGroovyXmlWebContextLoader detect a default location based on the name of the test class. If your class is named com.example.MyTest, the Groovy context loader will load your application context from "classpath:com/example/MyTestContext.groovy".

如果你同时省略@ContextConfiguration中的locationvalue属性,TestContext框架将会试图探测默认的Groovy脚本。特定的,GenericGroovyXmlContextLoaderGenericGroovyXmlWebContextLoader探测默认的位置根据测试类的名字。如果你的类名是com.example.MyTest,那么Groovy上下文加载器将会默认加载"classpath:com/example/MyTestContext.groovy"

 

package com.example;

 

@RunWith(SpringRunner.class)

// ApplicationContext will be loaded from

// "classpath:com/example/MyTestContext.groovy"

@ContextConfiguration

public class MyTest {

    // class body...

}

 

[Tip]

提示

 

Both XML configuration files and Groovy scripts can be declared simultaneously via the locations or value attribute of @ContextConfiguration. If the path to a configured resource location ends with .xml it will be loaded using an XmlBeanDefinitionReader; otherwise it will be loaded using a GroovyBeanDefinitionReader.

xml配置文件和Groovy脚本都可以被同时探测通过@ContextConfigurationlocationvalue属性。如果路径资源的配置结尾是.xml将使用XmlBeanDefinitionReader,否则将使用GroovyBeanDefinitionReader

 

The following listing demonstrates how to combine both in an integration test.

下面的列表描述了在集成测试中如何合并。

 

@RunWith(SpringRunner.class)

// ApplicationContext will be loaded from

// "/app-config.xml" and "/TestConfig.groovy"

@ContextConfiguration({ "/app-config.xml", "/TestConfig.groovy" })

public class MyTest {

    // class body...

}

 

Context configuration with annotated classes

使用注解类配置上下文

 

To load an ApplicationContext for your tests using annotated classes (see Section 7.12, Java-based container configuration), annotate your test class with @ContextConfiguration and configure the classes attribute with an array that contains references to annotated classes.

为了使用注解类加载ApplicationContext(将7.12节“基于java的容器配置”),注解你的测试类使用@ContextConfiguration并配置classes属性通过一个包含注解类的数组。

 

@RunWith(SpringRunner.class)

// ApplicationContext will be loaded from AppConfig and TestConfig

@ContextConfiguration(classes = {AppConfig.class, TestConfig.class})

public class MyTest {

    // class body...

}

 

[Tip]

提示

 

The term annotated class can refer to any of the following.

这些注解类可以引用如下:

 

    A class annotated with @Configuration

一个类使用了@Configuration来修饰

    A component (i.e., a class annotated with @Component, @Service, @Repository, etc.)

一个组件(例如,一个类使用了@Component@Service@Repository

    A JSR-330 compliant class that is annotated with javax.inject annotations

一个符合JSR-330的类使用了javax.inject注解

    Any other class that contains @Bean-methods

任何其他类包含@Bean-methods

 

Consult the javadocs of @Configuration and @Bean for further information regarding the configuration and semantics of annotated classes, paying special attention to the discussion of `@Bean` Lite Mode.

查看@Configurationjavadocs@Bean来了解更多信息有关配置和注解类的语义,并特别关注@Bean Lite模式的讨论。

 

If you omit the classes attribute from the @ContextConfiguration annotation, the TestContext framework will attempt to detect the presence of default configuration classes. Specifically, AnnotationConfigContextLoader and AnnotationConfigWebContextLoader will detect all static nested classes of the test class that meet the requirements for configuration class implementations as specified in the @Configuration javadocs. In the following example, the OrderServiceTest class declares a static nested configuration class named Config that will be automatically used to load the ApplicationContext for the test class. Note that the name of the configuration class is arbitrary. In addition, a test class can contain more than one static nested configuration class if desired.

如果你省略了@ContextConfigurationclasses属性,TestContext框架将会试图探测默认的配置类。特定的,AnnotationConfigContextLoaderAnnotationConfigWebContextLoader将探测测试类中所有的静态内置类来符合配置类的需求,配置类实现按照@Configurationjavadocs中。在下面的例子中,OrederServiceTest类定义了一个静态内部配置类名为COnfig将被自动使用来为这个类加载ApplicationContext。注意配置类的名字是任意的,一个测试类可以有多个静态内部配置类。

 

@RunWith(SpringRunner.class)

// ApplicationContext will be loaded from the

// static nested Config class

@ContextConfiguration

public class OrderServiceTest {

 

    @Configuration

    static class Config {

 

        // this bean will be injected into the OrderServiceTest class

        @Bean

        public OrderService orderService() {

            OrderService orderService = new OrderServiceImpl();

            // set properties, etc.

            return orderService;

        }

    }

 

    @Autowired

    private OrderService orderService;

 

    @Test

    public void testOrderService() {

        // test the orderService

    }

 

}

 

Mixing XML, Groovy scripts, and annotated classes

混合使用xmlgroovy脚本和注解类

 

It may sometimes be desirable to mix XML configuration files, Groovy scripts, and annotated classes (i.e., typically @Configuration classes) to configure an ApplicationContext for your tests. For example, if you use XML configuration in production, you may decide that you want to use @Configuration classes to configure specific Spring-managed components for your tests, or vice versa.

有时需要混合使用xmlgroovy脚本和注解类(例如,通常@Configuration类)类配置ApplicationContext为你的测试类。例如,如果你使用xml配置在生产中,你可以决定你希望使用@Configuration类来配置特定的spring管理组件为你的测试、反之亦然。

 

Furthermore, some third-party frameworks (like Spring Boot) provide first-class support for loading an ApplicationContext from different types of resources simultaneously (e.g., XML configuration files, Groovy scripts, and @Configuration classes). The Spring Framework historically has not supported this for standard deployments. Consequently, most of the SmartContextLoader implementations that the Spring Framework delivers in the spring-test module support only one resource type per test context; however, this does not mean that you cannot use both. One exception to the general rule is that the GenericGroovyXmlContextLoader and GenericGroovyXmlWebContextLoader support both XML configuration files and Groovy scripts simultaneously. Furthermore, third-party frameworks may choose to support the declaration of both locations and classes via @ContextConfiguration, and with the standard testing support in the TestContext framework, you have the following options.

此外,一些第三方框架(例如spring boot)提供优秀的支持同时对于加载ApplicationContext来自不同的资源(例如,xml配置文件、Groovy脚本和@Configuration类)。spring框架在以前没有对此的支持。因此大部分SmartContextLoader的实现在spring框架中例如spring-test模块中只支持每个上下文加载一种资源,然而这并不意味着你不能同时使用多个。一个通用的规则是GenericGroovyXmlContextLoaderGenericGroovyXmlWebContextLoader同时支持xml配置文件和groovy脚本。此外,第三方框架可以选择支持location和类通过@ContextConfiguration,并且标准的测试支持在TestContext框架,你可以有如下的选择。

 

If you want to use resource locations (e.g., XML or Groovy) and @Configuration classes to configure your tests, you will have to pick one as the entry point, and that one will have to include or import the other. For example, in XML or Groovy scripts you can include @Configuration classes via component scanning or define them as normal Spring beans; whereas, in a @Configuration class you can use @ImportResource to import XML configuration files or Groovy scripts. Note that this behavior is semantically equivalent to how you configure your application in production: in production configuration you will define either a set of XML or Groovy resource locations or a set of @Configuration classes that your production ApplicationContext will be loaded from, but you still have the freedom to include or import the other type of configuration.

如果你希望使用资源路径(例如xmlgroovy)和@Configuration类来配置你的测试,你可以选择一个作为入口,那一个将包含引入另外一个。例如,在xmlgroovy脚本中你可以包含@Configuration类通过组件扫描定义作为普通的springbean。在@Configuration类你可以使用@ImportResource来导入xml配置文件或groovy脚本。注意这种行为和你配置你的应用在生产上是一样的,在生产配置中你将定义一个xml活儿groovy资源位置的集合或@Configuration类的集合使得你的生产ApplicationContext将可以加载,但是你将资源包括或导入其他类型的配置。

 

Context configuration with context initializers

使用上下文初始化来配置上下文

 

To configure an ApplicationContext for your tests using context initializers, annotate your test class with @ContextConfiguration and configure the initializers attribute with an array that contains references to classes that implement ApplicationContextInitializer. The declared context initializers will then be used to initialize the ConfigurableApplicationContext that is loaded for your tests. Note that the concrete ConfigurableApplicationContext type supported by each declared initializer must be compatible with the type of ApplicationContext created by the SmartContextLoader in use (i.e., typically a GenericApplicationContext). Furthermore, the order in which the initializers are invoked depends on whether they implement Springs Ordered interface or are annotated with Springs @Order annotation or the standard @Priority annotation.

为了配置ApplicationContext对于你的测试你可以使用上下文初始化,使用@ContextConfiguration来修饰或配置initializers属性包含类的引用,这些类实现了ApplicationContextInitializer接口。定义上下文初始化将被使用来初始化ConfigurableApplicationContext用于你的测试。注意ConfigurableApplicationContext类型通过每个定义的初始化器被支持必须符合SmartContextLoader创建的ApplicationContext类型。(例如,通常是GenericApplicationContext)。此外,初始化器调用的顺序依赖于他们实现Ordered接口或使用spring@Order注解修饰或标准的@Priority注解。

 

@RunWith(SpringRunner.class)

// ApplicationContext will be loaded from TestConfig

// and initialized by TestAppCtxInitializer

@ContextConfiguration(

    classes = TestConfig.class,

    initializers = TestAppCtxInitializer.class)

public class MyTest {

    // class body...

}

 

It is also possible to omit the declaration of XML configuration files, Groovy scripts, or annotated classes in @ContextConfiguration entirely and instead declare only ApplicationContextInitializer classes which are then responsible for registering beans in the context — for example, by programmatically loading bean definitions from XML files or configuration classes.

也可以省略xml配置文件的定义、groovy脚本或注解类在@ContextConfiguration中并替代ApplicationContextInitializer类对于注册bean在上下文中————例如通过编程加载bean的定义从xml文件中或配置类中。

 

@RunWith(SpringRunner.class)

// ApplicationContext will be initialized by EntireAppInitializer

// which presumably registers beans in the context

@ContextConfiguration(initializers = EntireAppInitializer.class)

public class MyTest {

    // class body...

}

 

Context configuration inheritance

上下文配置的继承

 

@ContextConfiguration supports boolean inheritLocations and inheritInitializers attributes that denote whether resource locations or annotated classes and context initializers declared by superclasses should be inherited. The default value for both flags is true. This means that a test class inherits the resource locations or annotated classes as well as the context initializers declared by any superclasses. Specifically, the resource locations or annotated classes for a test class are appended to the list of resource locations or annotated classes declared by superclasses. Similarly, the initializers for a given test class will be added to the set of initializers defined by test superclasses. Thus, subclasses have the option of extending the resource locations, annotated classes, or context initializers.

@ContextConfiguration支持布尔类型的inheritLocationsinheritInitializers属性表示资源路径或注释类和定义在超类中的上下文初始化器是否可以被继承。默认值对于这两个值都是true。这意味着一个测试类继承资源位置或注解类作为上下文初始化器定义在任何一个超类中。特定的,资源位置或注解类被添加在资源位置或超类中注解类的列表中。相似的,初始化器对于给定的测试类将被添加初始化器的集合定义在测试的超类中。因此,超类可以有选择的扩展资源路径、注解类或上下文初始化器。

 

If the inheritLocations or inheritInitializers attribute in @ContextConfiguration is set to false, the resource locations or annotated classes and the context initializers, respectively, for the test class shadow and effectively replace the configuration defined by superclasses.

如果是@ContextConfiguration中的inheritLocationsinheritInitializers被设置为false,资源位置或注解类和上下文初始化器,明显的,对于测试类的配置将覆盖超类的配置。

 

In the following example that uses XML resource locations, the ApplicationContext for ExtendedTest will be loaded from "base-config.xml" and "extended-config.xml", in that order. Beans defined in "extended-config.xml" may therefore override (i.e., replace) those defined in "base-config.xml".

在下面的例子中使用了xml资源位置,ApplicationContext对于ExtendedTest将根据顺序加载"base-config.xml""extended-config.xml"。定义在"extended-config.xml"中的bean将被覆盖(例如,替换)那些定义在"base-config.xml"中的内容。

 

@RunWith(SpringRunner.class)

// ApplicationContext will be loaded from "/base-config.xml"

// in the root of the classpath

@ContextConfiguration("/base-config.xml")

public class BaseTest {

    // class body...

}

 

// ApplicationContext will be loaded from "/base-config.xml" and

// "/extended-config.xml" in the root of the classpath

@ContextConfiguration("/extended-config.xml")

public class ExtendedTest extends BaseTest {

    // class body...

}

 

Similarly, in the following example that uses annotated classes, the ApplicationContext for ExtendedTest will be loaded from the BaseConfig and ExtendedConfig classes, in that order. Beans defined in ExtendedConfig may therefore override (i.e., replace) those defined in BaseConfig.

相似的,在下面的例子中使用注解类,ExtendedTestApplicationContext将被按照顺序加载来自BaseConfigExtendedConfig类。在ExtendedConfig定义的bean将覆盖(例如,替换)定义在BaseConfig中的bean

 

@RunWith(SpringRunner.class)

// ApplicationContext will be loaded from BaseConfig

@ContextConfiguration(classes = BaseConfig.class)

public class BaseTest {

    // class body...

}

 

// ApplicationContext will be loaded from BaseConfig and ExtendedConfig

@ContextConfiguration(classes = ExtendedConfig.class)

public class ExtendedTest extends BaseTest {

    // class body...

}

 

In the following example that uses context initializers, the ApplicationContext for ExtendedTest will be initialized using BaseInitializer and ExtendedInitializer. Note, however, that the order in which the initializers are invoked depends on whether they implement Springs Ordered interface or are annotated with Springs @Order annotation or the standard @Priority annotation.

在下面的例子中使用了上下文初始化器,ExtendedTestApplicationContext将被初始化使用BaseInitializerExtendedInitializer。注意,初始化器的顺序被调用依赖于他们实现Ordered接口或注解使用spring@Order或标准的@Priority注解。

 

@RunWith(SpringRunner.class)

// ApplicationContext will be initialized by BaseInitializer

@ContextConfiguration(initializers = BaseInitializer.class)

public class BaseTest {

    // class body...

}

 

// ApplicationContext will be initialized by BaseInitializer

// and ExtendedInitializer

@ContextConfiguration(initializers = ExtendedInitializer.class)

public class ExtendedTest extends BaseTest {

    // class body...

}

 

Context configuration with environment profiles

使用环境配置文件配置上下文

 

Spring 3.1 introduced first-class support in the framework for the notion of environments and profiles (a.k.a., bean definition profiles), and integration tests can be configured to activate particular bean definition profiles for various testing scenarios. This is achieved by annotating a test class with the @ActiveProfiles annotation and supplying a list of profiles that should be activated when loading the ApplicationContext for the test.

spring3.1引入了优秀的支持在框架中对于环境和profile的处理(亦称bean定义配置文件),并且集成测试可以被配置激活特定bean定义配置文件对于不同的测试场景。这个被打包通过声明一个测试类使用@ActiveProfiles注解和提供一个配置文件列表应当被激活当为了测试加载ApplicationContext时。

 

[Note]

注意

 

@ActiveProfiles may be used with any implementation of the new SmartContextLoader SPI, but @ActiveProfiles is not supported with implementations of the older ContextLoader SPI.

@ActiveProfiles可以被使用配置新的SmartContextLoaderSPI的实现,但是@ActiveProfiles不被支持通过实现以前的ContextLoaderSPI

 

Lets take a look at some examples with XML configuration and @Configuration classes.

让我们看一些例子配合xml配置和@Configuration的类。

 

    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="...">

 

    

            class="com.bank.service.internal.DefaultTransferService">

        

        

    

 

    

            class="com.bank.repository.internal.JdbcAccountRepository">

        

    

 

    

        class="com.bank.service.internal.ZeroFeePolicy"/>

 

    

        

            

                location="classpath:com/bank/config/sql/schema.sql"/>

            

                location="classpath:com/bank/config/sql/test-data.sql"/>

        

    

 

    

        

    

 

    

        

            

                location="classpath:com/bank/config/sql/schema.sql"/>

        

    

 

 

package com.bank.service;

 

@RunWith(SpringRunner.class)

// ApplicationContext will be loaded from "classpath:/app-config.xml"

@ContextConfiguration("/app-config.xml")

@ActiveProfiles("dev")

public class TransferServiceTest {

 

    @Autowired

    private TransferService transferService;

 

    @Test

    public void testTransferService() {

        // test the transferService

    }

}

 

When TransferServiceTest is run, its ApplicationContext will be loaded from the app-config.xml configuration file in the root of the classpath. If you inspect app-config.xml youll notice that the accountRepository bean has a dependency on a dataSource bean; however, dataSource is not defined as a top-level bean. Instead, dataSource is defined three times: in the production profile, the dev profile, and the default profile.

TransferServiceTest正在运行,他的ApplicationContext将被加载从aop-config.xml配置文件中在classpath的根中。如果你查看app-config.xml文件你将注意到accountRepositorybean已经依赖一个dataSourcebean,然而dataSource并没有在顶级bean中定义。替代的dataSource定义在三次:在生产配置文件中、dev配置文件中和默认配置文件中。

 

By annotating TransferServiceTest with @ActiveProfiles("dev") we instruct the Spring TestContext Framework to load the ApplicationContext with the active profiles set to {"dev"}. As a result, an embedded database will be created and populated with test data, and the accountRepository bean will be wired with a reference to the development DataSource. And thats likely what we want in an integration test.

通过使用@ActiveProfiles("dev")来注解TransferServiceTest我们通知springTestContext框架来加载ApplicationContext配合激活的配置文件设置为{"dev"}。作为默认,内嵌的数据库将被创建和填充通过测试数据,并且accountRepositorybean将连接开发数据源的引用。就像我们希望在集成测试中的一样。

 

It is sometimes useful to assign beans to a default profile. Beans within the default profile are only included when no other profile is specifically activated. This can be used to define fallback beans to be used in the applications default state. For example, you may explicitly provide a data source for dev and production profiles, but define an in-memory data source as a default when neither of these is active.

有时为默认的配置指派一个bean是有意义的。只有当没有特定的配置被激活时默认配置才会被使用。这可以在应用默认的状态下用于定义后备的bean。例如,你可以明确的提供一个数据源为dev和生产的配置,定义内存数据源作为默认当没有配置被激活时。

 

The following code listings demonstrate how to implement the same configuration and integration test but using @Configuration classes instead of XML.

下面的代码展示了如何实现相同配置和集成测试只是使用了@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();

    }

 

}

 

package com.bank.service;

 

@RunWith(SpringRunner.class)

@ContextConfiguration(classes = {

        TransferServiceConfig.class,

        StandaloneDataConfig.class,

        JndiDataConfig.class,

        DefaultDataConfig.class})

@ActiveProfiles("dev")

public class TransferServiceTest {

 

    @Autowired

    private TransferService transferService;

 

    @Test

    public void testTransferService() {

        // test the transferService

    }

}

 

In this variation, we have split the XML configuration into four independent @Configuration classes:

在这种变化中,我们分离了xml配置到了独立的@Configuration类:

 

    TransferServiceConfig: acquires a dataSource via dependency injection using @Autowired

TransferServiceConfig:使用@Autowired的依赖注入获得一个数据源

    StandaloneDataConfig: defines a dataSource for an embedded database suitable for developer tests

StandaloneDataConfig:为开发者测试定义一个使用内置数据库的数据源

    JndiDataConfig: defines a dataSource that is retrieved from JNDI in a production environment

JndiDataConfig:在生产环境定义一个接收JNDI的数据源

    DefaultDataConfig: defines a dataSource for a default embedded database in case no profile is active

DefaultDataConfig:当没有配置被激活时定义一个默认内嵌的数据库作为数据源

 

As with the XML-based configuration example, we still annotate TransferServiceTest with @ActiveProfiles("dev"), but this time we specify all four configuration classes via the @ContextConfiguration annotation. The body of the test class itself remains completely unchanged.

由于基于xml配置的例子,我们依然使用@ActiveProfiles("dev")注释了TransferServiceTest,但是这次我们定义了所有的配置类通过@Configuration注解。测试类本身的内容完整被保留没有改变。

 

It is often the case that a single set of profiles is used across multiple test classes within a given project. Thus, to avoid duplicate declarations of the @ActiveProfiles annotation it is possible to declare @ActiveProfiles once on a base class, and subclasses will automatically inherit the @ActiveProfiles configuration from the base class. In the following example, the declaration of @ActiveProfiles (as well as other annotations) has been moved to an abstract superclass, AbstractIntegrationTest.

一个配置的简单集合可以在一个给定的工程中被使用在多个测试类中。因此,为了避免重复的定义@ActiveProfiles注解,在基类中定义@ActiveProfiles是重要的,并且子类将自动从基类中继承@ActiveProfiles配置。在下面的例子中,@ActiveProfiles的声明(和其他注解一样)已经移动到了抽象超类中,AbstractIntegrationTest

 

package com.bank.service;

 

@RunWith(SpringRunner.class)

@ContextConfiguration(classes = {

        TransferServiceConfig.class,

        StandaloneDataConfig.class,

        JndiDataConfig.class,

        DefaultDataConfig.class})

@ActiveProfiles("dev")

public abstract class AbstractIntegrationTest {

}

 

package com.bank.service;

 

// "dev" profile inherited from superclass

public class TransferServiceTest extends AbstractIntegrationTest {

 

    @Autowired

    private TransferService transferService;

 

    @Test

    public void testTransferService() {

        // test the transferService

    }

}

 

@ActiveProfiles also supports an inheritProfiles attribute that can be used to disable the inheritance of active profiles.

@ActiveProfiles也支持继承属性可以用于取消激活配置的继承。

 

package com.bank.service;

 

// "dev" profile overridden with "production"

@ActiveProfiles(profiles = "production", inheritProfiles = false)

public class ProductionTransferServiceTest extends AbstractIntegrationTest {

    // test body

}

 

Furthermore, it is sometimes necessary to resolve active profiles for tests programmatically instead of declaratively??for example, based on:

此外,有时需要编程处理激活的配置代替声明————例如,基于:

 

    the current operating system

当前的操作系统

    whether tests are being executed on a continuous integration build server

是否测试被运行在一个持续集成服务器

    the presence of certain environment variables

特定系统变量的处理

    the presence of custom class-level annotations

自定义类级别的注解

    etc.

等等

 

To resolve active bean definition profiles programmatically, simply implement a custom ActiveProfilesResolver and register it via the resolver attribute of @ActiveProfiles. The following example demonstrates how to implement and register a custom OperatingSystemActiveProfilesResolver. For further information, refer to the corresponding javadocs.

为了编程处理激活bean定义的配置,简单实现自定义的ActiveProfilesResolver并且使用@ActiveProfilesresolver属性进行注册。下面的例子展示了如何实现和注册自定义的OperatingSystemActiveProfilesResolver。关于更多的信息请查看相关的javadocs

 

package com.bank.service;

 

// "dev" profile overridden programmatically via a custom resolver

@ActiveProfiles(

    resolver = OperatingSystemActiveProfilesResolver.class,

    inheritProfiles = false)

public class TransferServiceTest extends AbstractIntegrationTest {

    // test body

}

 

package com.bank.service.test;

 

public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver {

 

    @Override

    String[] resolve(Class testClass) {

        String profile = ...;

        // determine the value of profile based on the operating system

        return new String[] {profile};

    }

}

 

Context configuration with test property sources

使用测试属性来配置上下文

 

Spring 3.1 introduced first-class support in the framework for the notion of an environment with a hierarchy of property sources, and since Spring 4.1 integration tests can be configured with test-specific property sources. In contrast to the @PropertySource annotation used on @Configuration classes, the @TestPropertySource annotation can be declared on a test class to declare resource locations for test properties files or inlined properties. These test property sources will be added to the set of PropertySources in the Environment for the ApplicationContext loaded for the annotated integration test.

spring3.1在框架中引入了对含有属性源的环境的声明的支持,并且自从spring4.1开始集成测试可以配置通过test-specific属性源。与@PropertySource注解用在@Configuration类来相比,@TestPropertySource注解可以定义在一个测试类来声明测试属性的资源位置或内置的属性。这些测试属性源将被添加到PropertySource的集合在环境中,对于使用注释的集成测试加载ApplicationContext

 

[Note]

注意

 

@TestPropertySource may be used with any implementation of the SmartContextLoader SPI, but @TestPropertySource is not supported with implementations of the older ContextLoader SPI.

@TestPropertySource可以配合使用任何实现的SmartContextLoaderSPI,但是@TestPropertySource不支持过期的ContextLoaderSPI实现。

 

Implementations of SmartContextLoader gain access to merged test property source values via the getPropertySourceLocations() and getPropertySourceProperties() methods in MergedContextConfiguration.

SmartContextLoader的实现访问合并的测试属性源值通过在MergedContextConfigurationgetPropertySourceLocations方法和getPropertySourceProperties方法。

 

Declaring test property sources

说明测试属性源

 

Test properties files can be configured via the locations or value attribute of @TestPropertySource as shown in the following example.

测试属性文件可以被配置通过@TestPropertySource中的locationsvalue属性如下面的例子。

 

Both traditional and XML-based properties file formats are supported — for example, "classpath:/com/example/test.properties" or "file:///path/to/file.xml".

传统和基于xml的配置文件格式都是被支持的————如:"classpath:/com/example/test.properties""file:///path/to/file.xml"

 

Each path will be interpreted as a Spring Resource. A plain path — for example, "test.properties" — will be treated as a classpath resource that is relative to the package in which the test class is defined. A path starting with a slash will be treated as an absolute classpath resource, for example: "/org/example/test.xml". A path which references a URL (e.g., a path prefixed with classpath:, file:, http:, etc.) will be loaded using the specified resource protocol. Resource location wildcards (e.g. */.properties) are not permitted: each location must evaluate to exactly one .properties or .xml resource.

每个路径将被当作一个spring的资源。一个普通的路径————例如,"test.properties"————将被作为一个classpath资源根据相对的包配合测试类定义的位置。以斜线开始的路径将被作为绝对classpath资源,例如,"/org/example/test.xml"。一个引用URL的路径(例如使用了特定的前缀,file:http:等等)将被加载使用特定的资源协议。使用通配符(例如,*/.properties)的资源路径是不被允许的:每个路径必须被解析为单个properties文件或单个xml资源。

 

@ContextConfiguration

@TestPropertySource("/test.properties")

public class MyIntegrationTests {

    // class body...

}

 

Inlined properties in the form of key-value pairs can be configured via the properties attribute of @TestPropertySource as shown in the following example. All key-value pairs will be added to the enclosing Environment as a single test PropertySource with the highest precedence.

内置的属性形如key-value的形式可以配置通过@TestPropertySourceproperties属性如下面的例子中展示。所有的key-value对将被添加到封闭的环境作为单个测试PropertySource有最高的优先级。

 

The supported syntax for key-value pairs is the same as the syntax defined for entries in a Java properties file:

key-value对的语法支持和定义在java属性文件中的实体一样:

 

    "key=value"

    "key:value"

    "key value"

 

@ContextConfiguration

@TestPropertySource(properties = {"timezone = GMT", "port: 4242"})

public class MyIntegrationTests {

    // class body...

}

 

Default properties file detection

默认属性文件的探测

 

If @TestPropertySource is declared as an empty annotation (i.e., without explicit values for the locations or properties attributes), an attempt will be made to detect a default properties file relative to the class that declared the annotation. For example, if the annotated test class is com.example.MyTest, the corresponding default properties file is "classpath:com/example/MyTest.properties". If the default cannot be detected, an IllegalStateException will be thrown.

如果@TestPropertySource被定义作为一个空的注解(例如,没有明确定义locationsproperties属性),将会尝试探测默认的属性文件根据定义的注解。例如,如果注解测试类是com.example.MyTest,相关的默认配置文件是"classpath:com/example/MyTest.properties"。如果默认的配置没有被发现,则会抛出IllegalStateException的异常。

 

Precedence

优先级

 

Test property sources have higher precedence than those loaded from the operating systems environment or Java system properties as well as property sources added by the application declaratively via @PropertySource or programmatically. Thus, test property sources can be used to selectively override properties defined in system and application property sources. Furthermore, inlined properties have higher precedence than properties loaded from resource locations.

从操作系统环境加载或java系统属性中加载作为测试属性源,同定义在@PropertySource中或编程定义的测试属性源。因此,测试属性源可以有选择的覆盖定义的系统和应用属性源中的属性值。此外,从资源位置中加载的内置的属性拥有更高的优先级。

 

In the following example, the timezone and port properties as well as any properties defined in "/test.properties" will override any properties of the same name that are defined in system and application property sources. Furthermore, if the "/test.properties" file defines entries for the timezone and port properties those will be overridden by the inlined properties declared via the properties attribute.

在下面的例子中,timezoneport属性定义在"/test.properties"将覆盖同名的配置定义在系统或应用属性源中。此外,如果"/test.properties"文件定义的值将被覆盖如果properties属性中定义了相应的内容。

 

@ContextConfiguration

@TestPropertySource(

    locations = "/test.properties",

    properties = {"timezone = GMT", "port: 4242"}

)

public class MyIntegrationTests {

    // class body...

}

 

Inheriting and overriding test property sources

继承和覆盖测试属性源

 

@TestPropertySource supports boolean inheritLocations and inheritProperties attributes that denote whether resource locations for properties files and inlined properties declared by superclasses should be inherited. The default value for both flags is true. This means that a test class inherits the locations and inlined properties declared by any superclasses. Specifically, the locations and inlined properties for a test class are appended to the locations and inlined properties declared by superclasses. Thus, subclasses have the option of extending the locations and inlined properties. Note that properties that appear later will shadow (i.e.., override) properties of the same name that appear earlier. In addition, the aforementioned precedence rules apply for inherited test property sources as well.

@TestPropertySource支持booleaninheritLocationsinheritProperties属性表示是否资源路径中关于定义在超类中属性文件和内嵌属性是否被继承。对于这两个属性的默认值都是true。这意味着测试类继承超类的资源和内嵌的属性。特定的,测试类的资源位置和内嵌的属性拼接定义在超类的资源和内嵌属性中。因此,子类可以选择性的继承资源和内嵌属性。注意最后添加的属性将(覆盖)相同的前面定义的内容。此外,上面描述的内容对于集成测试一样有用。

 

If the inheritLocations or inheritProperties attribute in @TestPropertySource is set to false, the locations or inlined properties, respectively, for the test class shadow and effectively replace the configuration defined by superclasses.

如果在@TestPropertySource中的inheritLocationsinheritProperties属性被设置为false,资源或内嵌属性,相应的,对于测试类将不会继承自超类。

 

In the following example, the ApplicationContext for BaseTest will be loaded using only the "base.properties" file as a test property source. In contrast, the ApplicationContext for ExtendedTest will be loaded using the "base.properties" and "extended.properties" files as test property source locations.

在下面的例子中,BaseTestApplicationContext将被加载仅使用"base.properties"文件作为测试属性源。与此相反,ExtendedTestApplicationContext将同时使用"base.properties""extended.properties"文件作为测试属性源位置。

 

@TestPropertySource("base.properties")

@ContextConfiguration

public class BaseTest {

    // ...

}

 

@TestPropertySource("extended.properties")

@ContextConfiguration

public class ExtendedTest extends BaseTest {

    // ...

}

 

In the following example, the ApplicationContext for BaseTest will be loaded using only the inlined key1 property. In contrast, the ApplicationContext for ExtendedTest will be loaded using the inlined key1 and key2 properties.

在下面的例子中,BaseTestApplicationContext将只使用内嵌的key1属性。与此相反的,ExtendedTestApplicationContext将同时使用key1key2属性。

 

@TestPropertySource(properties = "key1 = value1")

@ContextConfiguration

public class BaseTest {

    // ...

}

 

@TestPropertySource(properties = "key2 = value2")

@ContextConfiguration

public class ExtendedTest extends BaseTest {

    // ...

}

 

Loading a WebApplicationContext

加载WebApplicationContext

 

Spring 3.2 introduced support for loading a WebApplicationContext in integration tests. To instruct the TestContext framework to load a WebApplicationContext instead of a standard ApplicationContext, simply annotate the respective test class with @WebAppConfiguration.

spring3.2引入了对于在集成测试中加载WebApplicationContext的支持。为了使得TestContext框架加载WebApplicationContext而不是标准的ApplicationContext,需要简单的声明相应的测试类通过@WebAppConfiguration

 

The presence of @WebAppConfiguration on your test class instructs the TestContext framework (TCF) that a WebApplicationContext (WAC) should be loaded for your integration tests. In the background the TCF makes sure that a MockServletContext is created and supplied to your tests WAC. By default the base resource path for your MockServletContext will be set to "src/main/webapp". This is interpreted as a path relative to the root of your JVM (i.e., normally the path to your project). If youre familiar with the directory structure of a web application in a Maven project, youll know that "src/main/webapp" is the default location for the root of your WAR. If you need to override this default, simply provide an alternate path to the @WebAppConfiguration annotation (e.g., @WebAppConfiguration("src/test/webapp")). If you wish to reference a base resource path from the classpath instead of the file system, just use Springs classpath: prefix.

在你的测试类使用@WebAppConfiguration使得TestContext框架(TCF)应当加载WebApplicationContextWAC)。在后台TCF使得MockServletContext被创建并支持你测试的WAC。对于MockServletContext的基础资源路径默认设置为"src/main/webapp"。可以理解为基于你的JVM的根路径的相对路径(也就是,对于你的项目的路径)。如果如果你熟悉在maven项目中web应用中的结构,你就会知道"src/main/webapp"是你war的默认根路径。如果你需要覆盖这个默认值,简单提供一个替代在@WebAppConfiguration注解中(例如,@WebAppConfiguration("src/test/webapp"))。如果你希望引用一个来自classpath中的基路径代替文件系统,只要使用classpath:前缀。

 

Please note that Springs testing support for WebApplicationContexts is on par with its support for standard ApplicationContexts. When testing with a WebApplicationContext you are free to declare XML configuration files, Groovy scripts, or @Configuration classes via @ContextConfiguration. You are of course also free to use any other test annotations such as @ActiveProfiles, @TestExecutionListeners, @Sql, @Rollback, etc.

注意spring的测试支持对于WebApplicationContext和支持标准ApplicationContext是一样的。当测试一个WebApplicationContext是你可以自由的定义xml配置文件、Groovy脚本或@Configuration类通过@ContextConfiguration注解。你也可以自由使用其他测试注解例如@ActiveProfiles@TestExecutionListeners@Sql@Rollback等等。

 

The following examples demonstrate some of the various configuration options for loading a WebApplicationContext.

下面的例子描述了一个配置用于加载WebApplicationContext

 

Conventions.

 

@RunWith(SpringRunner.class)

 

// defaults to "file:src/main/webapp"

@WebAppConfiguration

 

// detects "WacTests-context.xml" in same package

// or static nested @Configuration class

@ContextConfiguration

 

public class WacTests {

    //...

}

 

The above example demonstrates the TestContext frameworks support for convention over configuration. If you annotate a test class with @WebAppConfiguration without specifying a resource base path, the resource path will effectively default to "file:src/main/webapp". Similarly, if you declare @ContextConfiguration without specifying resource locations, annotated classes, or context initializers, Spring will attempt to detect the presence of your configuration using conventions (i.e., "WacTests-context.xml" in the same package as the WacTests class or static nested @Configuration classes).

上面的例子示范了TestContext框架对于规范由于配置的支持。如果你使用@WebAppConfiguration来修饰你的测试类并且没有定义资源基路径,资源路径将影响默认值“file:src/main/webapp”。同样的,如果你使用@ContextConfiguration而没有定义资源路径、注解类或上下文初始化器,spring将尝试探测你的配置使用规范(也就是,作为WacTests类或静态内置@Configuration类相同包中的"WacTests-context.xml")。

 

Default resource semantics.

默认的资源语义

 

@RunWith(SpringRunner.class)

 

// file system resource

@WebAppConfiguration("webapp")

 

// classpath resource

@ContextConfiguration("/spring/test-servlet-config.xml")

 

public class WacTests {

    //...

}

 

This example demonstrates how to explicitly declare a resource base path with @WebAppConfiguration and an XML resource location with @ContextConfiguration. The important thing to note here is the different semantics for paths with these two annotations. By default, @WebAppConfiguration resource paths are file system based; whereas, @ContextConfiguration resource locations are classpath based.

这个例子展示了如何定义一个资源基路径通过@WebAppConfiguration@ContextConfiguration中的xml资源位置。在这里重要的是对于这两个注解对于路径的不同语义。默认情况下,@WebAppConfiguration资源路径是基于文件系统的,@ContextConfiguration资源路径是基于classpath的。

 

Explicit resource semantics.

明确资源语义

 

@RunWith(SpringRunner.class)

 

// classpath resource

@WebAppConfiguration("classpath:test-web-resources")

 

// file system resource

@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml")

 

public class WacTests {

    //...

}

 

In this third example, we see that we can override the default resource semantics for both annotations by specifying a Spring resource prefix. Contrast the comments in this example with the previous example.

在这个例子中,我们看到我们可以覆盖默认的资源语义通过特定的spring资源前缀。请与前面例子中的注释进行对比。

 

To provide comprehensive web testing support, Spring 3.2 introduced a ServletTestExecutionListener that is enabled by default. When testing against a WebApplicationContext this TestExecutionListener sets up default thread-local state via Spring Webs RequestContextHolder before each test method and creates a MockHttpServletRequest, MockHttpServletResponse, and ServletWebRequest based on the base resource path configured via @WebAppConfiguration. ServletTestExecutionListener also ensures that the MockHttpServletResponse and ServletWebRequest can be injected into the test instance, and once the test is complete it cleans up thread-local state.

为了提供全面的web测试支持,spring3.2引入了ServletTestExecutionListener并且默认启用。当测试一个WebApplicationContext时这个TestExecutionListener设置为默认本地线程状态通过springwebRequestContextHolder在每个测试方法之前,并且创建MockHttpServletRequestMockHttpServletResponseServletWebRequest基于通过@WebAppConfiguration配置的资源路径。ServletTestExecutionListener也允许MockHttpServletResponseServletWebRequest可以被注入到测试实例中,并且一旦测试完成则将清理本地线程状态。

 

Once you have a WebApplicationContext loaded for your test you might find that you need to interact with the web mocks — for example, to set up your test fixture or to perform assertions after invoking your web component. The following example demonstrates which mocks can be autowired into your test instance. Note that the WebApplicationContext and MockServletContext are both cached across the test suite; whereas, the other mocks are managed per test method by the ServletTestExecutionListener.

一旦你为你的测试加载了WebApplicationContext,你会发现他将和web模拟相互作用————例如,为了设置你的测试工具或执行断言在调用你的web组件之后。下面的例子展示了哪个模拟会被注入到你的测试实例中。注意WebApplicationContextMockServletContext都是在测试之间缓存的,然而其他的模拟通过ServletTestExecutionListener被每个测试方法来管理。

 

Injecting mocks.

模拟注入

 

@WebAppConfiguration

@ContextConfiguration

public class WacTests {

 

    @Autowired

    WebApplicationContext wac; // cached

 

    @Autowired

    MockServletContext servletContext; // cached

 

    @Autowired

    MockHttpSession session;

 

    @Autowired

    MockHttpServletRequest request;

 

    @Autowired

    MockHttpServletResponse response;

 

    @Autowired

    ServletWebRequest webRequest;

 

    //...

}

 

Context caching

上下文缓存

 

Once the TestContext framework loads an ApplicationContext (or WebApplicationContext) for a test, that context will be cached and reused for all subsequent tests that declare the same unique context configuration within the same test suite. To understand how caching works, it is important to understand what is meant by unique and test suite.

一旦TestContext框架为一个测试加载了ApplicationContext(或WebApplicationContext),上下文将被缓存并且对于后续的定义在相同上下文配置中的测试可用,共享相同的测试内容。为了理解缓存如何工作,主要是理解什么是测试套件的唯一。

 

An ApplicationContext can be uniquely identified by the combination of configuration parameters that are used to load it. Consequently, the unique combination of configuration parameters are used to generate a key under which the context is cached. The TestContext framework uses the following configuration parameters to build the context cache key:

一个ApplicationContext可以被唯一定义通过被加载的配置参数的组合。因此,唯一的配置参数的组合被用于生成一个key并被上下文缓存。TestContext框架使用下面的配置参数来构建上下文缓存key

 

    locations (from @ContextConfiguration)

    classes (from @ContextConfiguration)

    contextInitializerClasses (from @ContextConfiguration)

    contextCustomizers (from ContextCustomizerFactory)

    contextLoader (from @ContextConfiguration)

    parent (from @ContextHierarchy)

    activeProfiles (from @ActiveProfiles)

    propertySourceLocations (from @TestPropertySource)

    propertySourceProperties (from @TestPropertySource)

    resourceBasePath (from @WebAppConfiguration)

 

For example, if TestClassA specifies {"app-config.xml", "test-config.xml"} for the locations (or value) attribute of @ContextConfiguration, the TestContext framework will load the corresponding ApplicationContext and store it in a static context cache under a key that is based solely on those locations. So if TestClassB also defines {"app-config.xml", "test-config.xml"} for its locations (either explicitly or implicitly through inheritance) but does not define @WebAppConfiguration, a different ContextLoader, different active profiles, different context initializers, different test property sources, or a different parent context, then the same ApplicationContext will be shared by both test classes. This means that the setup cost for loading an application context is incurred only once (per test suite), and subsequent test execution is much faster.

例如,如果TestClasA定义了{"app-config.xml", "test-config.xml"}作为@ContextConfigurationlocations(或value),TestContext框架将加载相应的ApplicationContext并存储他们到一个静态的上下文缓存中并配合一个key基于他们的位置。因此如果TestClassB也定义了{"app-config.xml", "test-config.xml"}作为location(或有明确或不明确的继承关系)但是没有定义@WebAppConfiguration,一个不同上下文加载器,不同的激活配置,不同的上下文初始化器,不同的测试属性源,或不同的父上下文,那么相同的ApplicationContext将被这两个测试类所共享。这意味着对于加载应用上下文的消耗会只有一次(每个测试套件),并且后续的测试执行会很快。

 

[Note]

注意

 

The Spring TestContext framework stores application contexts in a static cache. This means that the context is literally stored in a static variable. In other words, if tests execute in separate processes the static cache will be cleared between each test execution, and this will effectively disable the caching mechanism.

springTestContext框架存储应用上下文在一个静态的缓存中。这意味着上下文是以字面的形式存储到静态变量中的。换句话说,如果测试在另一个进程中执行,静态缓存将被清空对于每个测试执行并且将影响关闭缓存策略。

 

To benefit from the caching mechanism, all tests must run within the same process or test suite. This can be achieved by executing all tests as a group within an IDE. Similarly, when executing tests with a build framework such as Ant, Maven, or Gradle it is important to make sure that the build framework does not fork between tests. For example, if the forkMode for the Maven Surefire plug-in is set to always or pertest, the TestContext framework will not be able to cache application contexts between test classes and the build process will run significantly slower as a result.

为了从缓存策略中获益,所有的测试必须在相同的进程或测试套件中运行。这可以通过在IDE中作为组来执行所有的测试。类似的,如果通过AntMavenGradle来构建框架执行测试,重要的是保证构建框架没有在测试之间被分开。例如,如果Maven中的forkMode被设置为alwayspertestTestContext框架就不能缓存应用上下文在测试类之间并且构建进程将运行很慢。

 

Since Spring Framework 4.3, the size of the context cache is bounded with a default maximum size of 32. Whenever the maximum size is reached, a least recently used (LRU) eviction policy is used to evict and close stale contexts. The maximum size can be configured from the command line or a build script by setting a JVM system property named spring.test.context.cache.maxSize. As an alternative, the same property can be set programmatically via the SpringProperties API.

自从spring框架4.3,上下文缓存的大小的默认值是32.当达到最大值,LRU算法会被使用来处理上下文的状态。最大值可以通过命令行或兼并配置通过设置JVM系统属性名字为spring.test.context.cache.maxSize。另外,相同的属性也可以通过SpringPropertiesAPI编程设置。

 

Since having a large number of application contexts loaded within a given test suite can cause the suite to take an unnecessarily long time to execute, it is often beneficial to know exactly how many contexts have been loaded and cached. To view the statistics for the underlying context cache, simply set the log level for the org.springframework.test.context.cache logging category to DEBUG.

自从有了大量应用上下文被加载一个给定的测试套件可以引起长时间不被执行,这是有益的了解有多少上下文被加载并缓存。为了查看上下文缓冲的状态,简单的设置日志的级别对于org.springframework.test.context.cachedebug

 

In the unlikely case that a test corrupts the application context and requires reloading — for example, by modifying a bean definition or the state of an application object — you can annotate your test class or test method with @DirtiesContext (see the discussion of @DirtiesContext in Section 15.4.1, Spring Testing Annotations). This instructs Spring to remove the context from the cache and rebuild the application context before executing the next test. Note that support for the @DirtiesContext annotation is provided by the DirtiesContextBeforeModesTestExecutionListener and the DirtiesContextTestExecutionListener which are enabled by default.

一个测试破坏应用上下文导致需要重新启动的情况基本不存在————例如,通过修改一个bean定义或应用object的状态————你可以注解你的测试类或测试方法通过@DirtiesContext(将15.4.1中有关@DirtiesContext的讨论,“spring的测试注解”)。这使得spring从缓存中移除上下文并重新构建上下文在执行下一个测试之前。注意对于@DirtiesContext的支持是通过DirtiesContextBeforeModesTestExecutionListenerDirtiesContextTestExecutionListener来提供的,并且作为默认值。

 

Context hierarchies

上下文结构

 

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

当基于加载springApplicationContext来书写集成测试时,通常对于一个测试是足够的,然而,有时需要了解ApplicationContext的层级对于测试是有益的。例如,如果你开发一个springmvcweb应用你将需要加载WebApplicationContext通过springContextLoaderListener和一个DispatcherServlet。这会导致父子上下文结构共享组件和基础配置,定义在根上下文并在特定的web组件中被使用。另一个使用案例可以在spirngBatch应用中被找到当你有一个父上下文提供配置对于共享的批处理架构和子上下文对于特定的批处理任务。

 

Since Spring Framework 3.2.2, it is possible to write integration tests that use context hierarchies by declaring context configuration via the @ContextHierarchy annotation, either on an individual test class or within a test class hierarchy. If a context hierarchy is declared on multiple classes within a test class hierarchy it is also possible to merge or override the context configuration for a specific, named level in the context hierarchy. When merging configuration for a given level in the hierarchy the configuration resource type (i.e., XML configuration files or annotated classes) must be consistent; otherwise, it is perfectly acceptable to have different levels in a context hierarchy configured using different resource types.

自从spring框架3.2.2,可以书写集成测试通过使用上下文结构定义上下文配置通过@ContextHierarchy注解,或者修饰独立的测试类或测试类结构。如果一个上下文结构定义了多个类配合测试类结构也可以合并或覆盖上下文配置对于特定、name级别在上下文结构中。当对于给定的级别混合配置在配置资源类型中(也就是,xml配置文件或注解类)必须是一致的,此外这也适用于使用不同资源类型的上下文结构配合不同的级别。

 

The following JUnit 4 based examples demonstrate common configuration scenarios for integration tests that require the use of context hierarchies.

下面基于JUnit4的例子描述了通用的配置场景对于集成测试需要上下文结构。

 

ControllerIntegrationTests represents a typical integration testing scenario for a Spring MVC web application by declaring a context hierarchy consisting of two levels, one for the root WebApplicationContext (loaded using the TestAppConfig @Configuration class) and one for the dispatcher servlet WebApplicationContext (loaded using the WebConfig @Configuration class). The WebApplicationContext that is autowired into the test instance is the one for the child context (i.e., the lowest context in the hierarchy).

ControllerIntegrationTests代表一个典型的集成测试场景对于springmvcweb应用通过定义上下文结构包含两个级别,一个用于根WebApplicationContext(使用TestAppConfig@Configuration类来加载)并且另一个用于dispatcherservletWebApplicationContext(通过使用WebConfig@Configuration类来加载)。WebApplicationContext被注入到测试实例中是用于子上下文(也就是说,最低的上下文在结构中)。

 

@RunWith(SpringRunner.class)

@WebAppConfiguration

@ContextHierarchy({

    @ContextConfiguration(classes = TestAppConfig.class),

    @ContextConfiguration(classes = WebConfig.class)

})

public class ControllerIntegrationTests {

 

    @Autowired

    private WebApplicationContext wac;

 

    // ...

}

 

The following test classes define a context hierarchy within a test class hierarchy. AbstractWebTests declares the configuration for a root WebApplicationContext in a Spring-powered web application. Note, however, that AbstractWebTests does not declare @ContextHierarchy; consequently, subclasses of AbstractWebTests can optionally participate in a context hierarchy or simply follow the standard semantics for @ContextConfiguration. SoapWebServiceTests and RestWebServiceTests both extend AbstractWebTests and define a context hierarchy via @ContextHierarchy. The result is that three application contexts will be loaded (one for each declaration of @ContextConfiguration), and the application context loaded based on the configuration in AbstractWebTests will be set as the parent context for each of the contexts loaded for the concrete subclasses.

下面的测试类定义了个测试类结构。AbstractWebTests定义了配置在springweb应用中为了根WebApplicationContext。注意,然而AbstractWebTests没有定义@ContextHierarchy,因此AbstractWebTests的子类可以选择加入到上下文结构中或简单跟随基本的@Configuration语义。SoapWebServiceTestsRestWebServiceTests都继承自AbstractWebTests并且定义了上下文结构通过@ContextHierarchy。结果就是三个应用上下文将被加载(一个用于@ContextConfiguration的定义),并且应用上下文加载基于AbstractWebTests中的配置将被设置为每个被加载的上下文的父上下文对于混合子类。

 

@RunWith(SpringRunner.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 {}

 

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

下面的类展示了命名结构的使用用于合并特定级别在上下文结构中的配置。BaseTests定义了两个级别在结构中,父和子。ExtendedTests扩展了BaseTests并且命令springTestContext框架来合并上下文配置为子结构级别,保证名字的定义通过@ContextConfigurationname属性都是“后代”。这三个应用上下文的结果将被加载:一个是"/app-config.xml"、一个是"/user-config.xml",一个是{"/user-config.xml", "/order-config.xml"}。和前面的例子相比,从"/app-config.xml"加载的应用上下文将被设置作为父上下文用于上下文加载来自"/user-config.xml"{"/user-config.xml", "/order-config.xml"}

 

@RunWith(SpringRunner.class)

@ContextHierarchy({

    @ContextConfiguration(name = "parent", locations = "/app-config.xml"),

    @ContextConfiguration(name = "child", locations = "/user-config.xml")

})

public class BaseTests {}

 

@ContextHierarchy(

    @ContextConfiguration(name = "child", locations = "/order-config.xml")

)

public class ExtendedTests extends BaseTests {}

 

In contrast to the previous example, this example demonstrates how to override the configuration for a given named level in a context hierarchy by setting the inheritLocations flag in @ContextConfiguration to false. Consequently, the application context for ExtendedTests will be loaded only from "/test-user-config.xml" and will have its parent set to the context loaded from "/app-config.xml".

和前面的例子相比,这个例子描述了如何覆盖配置根据给定的命名级别,在上下文结构中设置了@ContextConfiguration中的inheritLocations标识为false。从而,ExtendedTests的上下文将只从"/test-user-config.xml"中加载并且将有父集合从"/app-config.xml"中加载的上下文。

 

@RunWith(SpringRunner.class)

@ContextHierarchy({

    @ContextConfiguration(name = "parent", locations = "/app-config.xml"),

    @ContextConfiguration(name = "child", locations = "/user-config.xml")

})

public class BaseTests {}

 

@ContextHierarchy(

    @ContextConfiguration(

        name = "child",

        locations = "/test-user-config.xml",

        inheritLocations = false

))

public class ExtendedTests extends BaseTests {}

 

[Note]

 

If @DirtiesContext is used in a test whose context is configured as part of a context hierarchy, the hierarchyMode flag can be used to control how the context cache is cleared. For further details consult the discussion of @DirtiesContext in Spring Testing Annotations and the @DirtiesContext javadocs.

如果@DirtiesContext被用于测试中,上下文被配置作为上下文结构的一部分,hierarchyMode标志可以用于控制上下文缓存是否是干净的。更多细节请参考springTesting注解有关@DirtiesContext的讨论和@DirtiesContextjavadocs

 

15.5.5 Dependency injection of test fixtures

测试工具的依赖注入

 

When you use the DependencyInjectionTestExecutionListener — which is configured by default — the dependencies of your test instances are injected from beans in the application context that you configured with @ContextConfiguration. You may use setter injection, field injection, or both, depending on which annotations you choose and whether you place them on setter methods or fields. For consistency with the annotation support introduced in Spring 2.5 and 3.0, you can use Springs @Autowired annotation or the @Inject annotation from JSR 330.

当你使用DependencyInjectionTestExecutionListener————被配置为默认值————你测试实例的依赖被注入来自应用上下文中的bean,配置于@ContextConfiguration中。你可以使用setter方法注入、field注入或两者同时使用,依赖于你选择什么样的注解和你在什么位置使用set方法和field。关于注解支持的一致性在spring2.53.0中被引入,你可以使用spring@Autowired注解或@Inject注解来自JSR330.

 

[Tip]

提示

 

The TestContext framework does not instrument the manner in which a test instance is instantiated. Thus the use of @Autowired or @Inject for constructors has no effect for test classes.

TestContext框架不指定测试实例的实例化。使用@Autowired@Inject对于测试类来说没有什么区别。

 

Because @Autowired is used to perform autowiring by type, if you have multiple bean definitions of the same type, you cannot rely on this approach for those particular beans. In that case, you can use @Autowired in conjunction with @Qualifier. As of Spring 3.0 you may also choose to use @Inject in conjunction with @Named. Alternatively, if your test class has access to its ApplicationContext, you can perform an explicit lookup by using (for example) a call to applicationContext.getBean("titleRepository").

因为@Autowired被用于类型的注入,如果你有多个bean定义对于相同的类型,你不能依赖这种方式对于特定的某些bean。在这种情况,你可以使用@Autowired配合@Qualifier。在spring3.0中你也可以选择@Inject配合@Named。此外,如果你的测试类需要访问他的ApplicationContext,你可以查找使用(例如)调用applicationContext.getBean("titleRepository")

 

If you do not want dependency injection applied to your test instances, simply do not annotate fields or setter methods with @Autowired or @Inject. Alternatively, you can disable dependency injection altogether by explicitly configuring your class with @TestExecutionListeners and omitting DependencyInjectionTestExecutionListener.class from the list of listeners.

如果你不需要依赖注入对于你的测试实例,不需要使用@Autowired@Inject来修饰fieldset方法。相对的,你可以取消依赖注入通过显示配置你的类通过@TestExecutionListeners和省略监听器列表中的DependencyInjectionTestExecutionListener.class

 

Consider the scenario of testing a HibernateTitleRepository class, as outlined in the Goals section. The next two code listings demonstrate the use of @Autowired on fields and setter methods. The application context configuration is presented after all sample code listings.

考虑测试HibernateTitleRepository类的场景,作为概述在这一节。下面两段代码列出了@Autowired的使用在fieldset方法上。应用上下文配置在所有代码的最后被展示。

 

[Note]

注意

 

The dependency injection behavior in the following code listings is not specific to JUnit 4. The same DI techniques can be used in conjunction with any testing framework.

依赖注入的行为在下面的代码中并没有特定在JUnit4中。相同的依赖注入技术可以使用配合任何测试框架。

 

The following examples make calls to static assertion methods such as assertNotNull() but without prepending the call with Assert. In such cases, assume that the method was properly imported through an import static declaration that is not shown in the example.

下面的例子调用静态的断言方法例如assertNotNull但是没有使用Assert。在这些例子中,假设方法被正确导入通过静态的定义但是没有在例子中体现。

 

The first code listing shows a JUnit 4 based implementation of the test class that uses @Autowired for field injection.

第一个代码案例展示了JUnit4的基本实现有关测试类通过使用@Autowiredfield注入中。

 

@RunWith(SpringRunner.class)

// specifies the Spring configuration to load for this test fixture

@ContextConfiguration("repository-config.xml")

public class HibernateTitleRepositoryTests {

 

    // this instance will be dependency injected by type

    @Autowired

    private HibernateTitleRepository titleRepository;

 

    @Test

    public void findById() {

        Title title = titleRepository.findById(new Long(10));

        assertNotNull(title);

    }

}

 

Alternatively, you can configure the class to use @Autowired for setter injection as seen below.

此外,你可以配置类通过使用@Autowiredset方法注入如下面的例子。

 

@RunWith(SpringRunner.class)

// specifies the Spring configuration to load for this test fixture

@ContextConfiguration("repository-config.xml")

public class HibernateTitleRepositoryTests {

 

    // this instance will be dependency injected by type

    private HibernateTitleRepository titleRepository;

 

    @Autowired

    public void setTitleRepository(HibernateTitleRepository titleRepository) {

        this.titleRepository = titleRepository;

    }

 

    @Test

    public void findById() {

        Title title = titleRepository.findById(new Long(10));

        assertNotNull(title);

    }

}

 

The preceding code listings use the same XML context file referenced by the @ContextConfiguration annotation (that is, repository-config.xml), which looks like this:

之前的代码使用了相同的xml上下文文件,通过@ContextConfiguration来引入(就是repository-config.xml),其内容如下:

 

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans.xsd">

 

    

    

        

    

 

    

        

    

 

 

[Note]

注意

 

If you are extending from a Spring-provided test base class that happens to use @Autowired on one of its setter methods, you might have multiple beans of the affected type defined in your application context: for example, multiple DataSource beans. In such a case, you can override the setter method and use the @Qualifier annotation to indicate a specific target bean as follows, but make sure to delegate to the overridden method in the superclass as well.

如果你扩展spring提供的测试基类使用@Autowired在他们其中一个set方法,你可以有多个bean有关类型定义在你的应用上下文中:例如,多个DataSourcebean。在这个例子中,你可以覆盖set方法并使用@Qualifier注解来指定特定的目标bean如下,但是保证覆盖了超类的方法。

 

// ...

 

    @Autowired

    @Override

    public void setDataSource(@Qualifier("myDataSource") DataSource dataSource) {

        super.setDataSource(dataSource);

    }

 

// ...

 

The specified qualifier value indicates the specific DataSource bean to inject, narrowing the set of type matches to a specific bean. Its value is matched against declarations within the corresponding definitions. The bean name is used as a fallback qualifier value, so you may effectively also point to a specific bean by name there (as shown above, assuming that "myDataSource" is the bean id).

特定的qualifier值指示了特定的DataSource被注入,缩小了匹配特定bean的集合范围。他的值匹配的定义根据相应的定义。bean的名字被用于作为回调的qualifier值,因此你可以有效的指向特定的bean通过名字在这里(显示如上,假设"myDataSource"beanid

 

15.5.6 Testing request and session scoped beans

测试请求和session范围的bean

 

Request and session scoped beans have been supported by Spring since the early years, and since Spring 3.2 its a breeze to test your request-scoped and session-scoped beans by following these steps.

请求和会话范围的bean在之前就被spring支持,并且自从spring3.2,可以通过以下的步骤来支持请求和会话范围的bean

 

    Ensure that a WebApplicationContext is loaded for your test by annotating your test class with @WebAppConfiguration.

保证WebApplicationContext被加载对于你的测试注解类通过@WebAppConfiguration

    Inject the mock request or session into your test instance and prepare your test fixture as appropriate.

在你的测试实例中注入模拟的请求或会被并且准备好你适当的测试工具。

    Invoke your web component that you retrieved from the configured WebApplicationContext (i.e., via dependency injection).

调用你的web组件,你希望获得来自配置的WebApplicationContext(例如,依赖注入)

    Perform assertions against the mocks.

对模拟执行断言

 

The following code snippet displays the XML configuration for a login use case. Note that the userService bean has a dependency on a request-scoped loginAction bean. Also, the LoginAction is instantiated using SpEL expressions that retrieve the username and password from the current HTTP request. In our test, we will want to configure these request parameters via the mock managed by the TestContext framework.

下面的代码片段展示了xml配置对于登录用例的使用。注意userServicebean有一个依赖注入是请求范围的loginActionbean。并且LoginAction被实例化通过使用SpEL表达式并且接收用户名和密码来自当前的HTTP请求。在我们的测试中,我们将配置这些请求参数通过TestContext框架管理的模拟。

 

Request-scoped bean configuration.

请求范围的bean的配置

 

 

    

            class="com.example.SimpleUserService"

            c:loginAction-ref="loginAction" />

 

    

            c:username="{request.getParameter('user')}"

            c:password="{request.getParameter('pswd')}"

            scope="request">

        

    

 

 

In RequestScopedBeanTests we inject both the UserService (i.e., the subject under test) and the MockHttpServletRequest into our test instance. Within our requestScope() test method we set up our test fixture by setting request parameters in the provided MockHttpServletRequest. When the loginUser() method is invoked on our userService we are assured that the user service has access to the request-scoped loginAction for the current MockHttpServletRequest (i.e., the one we just set parameters in). We can then perform assertions against the results based on the known inputs for the username and password.

RequestScopedBeanTests中我们注入了UserService(也就是接受测试的)和MockHttpServletRequest对于我们测试的实例。在我们requestScope的测试方法中我们设置了我们的测试通过设置请求参数在提供的MockHttpServletRequest中。当loginUser方法被调用在我们的userService中时我们保证用户服务已经范文了来自当前MockHttpServletRequest(也就是我们设置参数的那一个)中的请求范围的loginAction。我们可以使用断言在结果上基于已知的用户名和密码的输入。

 

Request-scoped bean test.

请求范围的bean的测试

 

@RunWith(SpringRunner.class)

@ContextConfiguration

@WebAppConfiguration

public class RequestScopedBeanTests {

 

    @Autowired UserService userService;

    @Autowired MockHttpServletRequest request;

 

    @Test

    public void requestScope() {

 

        request.setParameter("user", "enigma");

        request.setParameter("pswd", "$pr!ng");

 

        LoginResults results = userService.loginUser();

 

        // assert results

    }

}

 

The following code snippet is similar to the one we saw above for a request-scoped bean; however, this time the userService bean has a dependency on a session-scoped userPreferences bean. Note that the UserPreferences bean is instantiated using a SpEL expression that retrieves the theme from the current HTTP session. In our test, we will need to configure a theme in the mock session managed by the TestContext framework.

下面的代码片段和我们之前见到的相似来自请求范围的bean;然而,这次userServicebean有一个依赖是会话范围的userPreferencesbean。注意UserPreferencesbean被初始化通过SpEL表达式并且接收来自当前的HTTP会话。在我们的测试中,我们将需要配置一个主题通过TestContext框架管理的模拟会话。

 

Session-scoped bean configuration.

会话范围的bean的配置

 

 

    

            class="com.example.SimpleUserService"

            c:userPreferences-ref="userPreferences" />

 

    

            class="com.example.UserPreferences"

            c:theme="#{session.getAttribute('theme')}"

            scope="session">

        

    

 

 

In SessionScopedBeanTests we inject the UserService and the MockHttpSession into our test instance. Within our sessionScope() test method we set up our test fixture by setting the expected "theme" attribute in the provided MockHttpSession. When the processUserPreferences() method is invoked on our userService we are assured that the user service has access to the session-scoped userPreferences for the current MockHttpSession, and we can perform assertions against the results based on the configured theme.

SessionScopedBeanTests中我们注入了UserServiceMockHttpSession到我们的测试实例中,在我们sessionScope测试方法中我们设置了我们的测试工具通过设置明确的theme属性在提供的MockHttpSession中。当processUserPreferences方法被调用在我们的userService中我们保证用户服务已经访问了会话范围的userPreferences对于当前的MockHttpSession并且我们可以使用断言基于当前的主题。

 

Session-scoped bean test.

会话范围的bean的测试

 

@RunWith(SpringRunner.class)

@ContextConfiguration

@WebAppConfiguration

public class SessionScopedBeanTests {

 

    @Autowired UserService userService;

    @Autowired MockHttpSession session;

 

    @Test

    public void sessionScope() throws Exception {

 

        session.setAttribute("theme", "blue");

 

        Results results = userService.processUserPreferences();

 

        // assert results

    }

}

 

15.5.7 Transaction management

事务管理

 

In the TestContext framework, transactions are managed by the TransactionalTestExecutionListener which is configured by default, even if you do not explicitly declare @TestExecutionListeners on your test class. To enable support for transactions, however, you must configure a PlatformTransactionManager bean in the ApplicationContext that is loaded via @ContextConfiguration semantics (further details are provided below). In addition, you must declare Springs @Transactional annotation either at the class or method level for your tests.

TestContext框架中,事务被管理通过TransactionalTestExecutionListener并且被配置为默认值,即使你没有明确的定义@TestExecutionListeners在你的测试类中。为了启用事务的支持,然而你必须配置PlatformTransactionManagerbeanApplicationContext中通过@ContextConfiguration语义加载(更多的信息在下面)。此外,你必须定义spring@Transactional注解在类或方法级别为你的测试。

 

Test-managed transactions

管理测试事务

 

Test-managed transactions are transactions that are managed declaratively via the TransactionalTestExecutionListener or programmatically via TestTransaction (see below). Such transactions should not be confused with Spring-managed transactions (i.e., those managed directly by Spring within the ApplicationContext loaded for tests) or application-managed transactions (i.e., those managed programmatically within application code that is invoked via tests). Spring-managed and application-managed transactions will typically participate in test-managed transactions; however, caution should be taken if Spring-managed or application-managed transactions are configured with any propagation type other than REQUIRED or SUPPORTS (see the discussion on transaction propagation for details).

管理测试事务是通过TransactionalTestExecutionListener管理的事务或通过TestTransaction的编程实现(如下)。这样的事务不会与spring管理的事务混淆(也就是说这些管理通过ApplicationContext来加载对于每个测试)或管理应用的事务(也就是说这些应用代码被测试来调用)。管理spring和管理应用的事务通常参与管理测试的事务,然而应当注意如果管理spring或管理应用的事务可以被任何类型配置不同于REQUIREDSUPPORTS(见有关讨论事务的章节)

 

Enabling and disabling transactions

开启和关闭事务

 

Annotating a test method with @Transactional causes the test to be run within a transaction that will, by default, be automatically rolled back after completion of the test. If a test class is annotated with @Transactional, each test method within that class hierarchy will be run within a transaction. Test methods that are not annotated with @Transactional (at the class or method level) will not be run within a transaction. Furthermore, tests that are annotated with @Transactional but have the propagation type set to NOT_SUPPORTED will not be run within a transaction.

使用@Transactional注解一个测试方法导致测试将在事务中允许,默认,将在测试完成后自动回滚。如果一个测试类被修饰通过@Transactional,每个测试方法将类结构中将被运行在事务中。测试方法没有被@Transactional修饰(在类或方法级别)将不会再事务中运行。此外,使用@Transactional修饰的测试但是使用了NOT_SUPPORTED将不会再事务中允许。

 

Note that AbstractTransactionalJUnit4SpringContextTests and AbstractTransactionalTestNGSpringContextTests are preconfigured for transactional support at the class level.

注意AbstractTransactionalJUnit4SpringContextTestsAbstractTransactionalTestNGSpringContextTests被配置在类级别对于事务支持。

 

The following example demonstrates a common scenario for writing an integration test for a Hibernate-based UserRepository. As explained in the section called Transaction rollback and commit behavior, there is no need to clean up the database after the createUser() method is executed since any changes made to the database will be automatically rolled back by the TransactionalTestExecutionListener. See Section 15.7, PetClinic Examplefor an additional example.

下面的例子描述了一个场景对于书写集成测试对于基于HibernateUserRepository。在章节中解释称为“事务回滚和提交行为”,不需要清理数据库在createUser方法执行之后自从任何改变对于数据库将被自动回滚通过TransactionalTestExecutionListener。将15.7节“PetClinic Example”来查看额外的例子。

 

@RunWith(SpringRunner.class)

@ContextConfiguration(classes = TestConfig.class)

@Transactional

public class HibernateUserRepositoryTests {

 

    @Autowired

    HibernateUserRepository repository;

 

    @Autowired

    SessionFactory sessionFactory;

 

    JdbcTemplate jdbcTemplate;

 

    @Autowired

    public void setDataSource(DataSource dataSource) {

        this.jdbcTemplate = new JdbcTemplate(dataSource);

    }

 

    @Test

    public 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);

    }

 

    protected int countRowsInTable(String tableName) {

        return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);

    }

 

    protected void assertNumUsers(int expected) {

        assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));

    }

}

 

Transaction rollback and commit behavior

事务回滚和提交行为

 

By default, test transactions will be automatically rolled back after completion of the test; however, transactional commit and rollback behavior can be configured declaratively via the @Commit and @Rollback annotations. See the corresponding entries in the annotation support section for further details.

默认,测试事务将自动回滚在测试完成之后,然而事务提交和回滚行为可以被显示配置通过@Commit@Rollback注解。见相关注解支持来了解更多。

 

Programmatic transaction management

编程事务管理

 

Since Spring Framework 4.1, it is possible to interact with test-managed transactions programmatically via the static methods in TestTransaction. For example, TestTransaction may be used within test methods, before methods, and after methods to start or end the current test-managed transaction or to configure the current test-managed transaction for rollback or commit. Support for TestTransaction is automatically available whenever the TransactionalTestExecutionListener is enabled.

自从spring框架4.1,可以通过TestTransaction中的静态方法来管理测试的事务。例如,TestTransaction可以被使用在测试方法中、方法之前和方法之后来开始或结束当前管理测试的事务或配置当前管理测试的事务回滚或提交。对于TestTransaction的支持可以自动的实现当启用TransactionalTestExecutionListener时。

 

The following example demonstrates some of the features of TestTransaction. Consult the javadocs for TestTransaction for further details.

下面的例子描述了一些TestTransaction的特性。查阅TestTransactionjavadocs来了解更多细节。

 

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

    }

}

 

Executing code outside of a transaction

在事务外执行代码

 

Occasionally you need to execute certain code before or after a transactional test method but outside the transactional context — for example, to verify the initial database state prior to execution of your test or to verify expected transactional commit behavior after test execution (if the test was configured to commit the transaction). TransactionalTestExecutionListener supports the @BeforeTransaction and @AfterTransaction annotations exactly for such scenarios. Simply annotate any void method in a test class or any void default method in a test interface with one of these annotations, and the TransactionalTestExecutionListener ensures that your before transaction method or after transaction method is executed at the appropriate time.

有时你需要在事务之前或之后执行代码————例如,为了初始化数据库状态指针来执行你的测试或在测试完成之后验证事务提交的行为(如果测试配置为提交事务)。TransactionalTestExecutionListener支持@BeforeTransaction@AfterTransaction注解对于这样的场景。简单注解你测试类中的方法或任何void的默认方法在测试接口使用其中一个注解,并且TransactionalTestExecutionListener保证之前的事务方法或事务方法之后将在适合的时间执行。

 

[Tip]

提示

 

Any before methods (such as methods annotated with JUnit 4s @Before) and any after methods (such as methods annotated with JUnit 4s @After) are executed within a transaction. In addition, methods annotated with @BeforeTransaction or @AfterTransaction are naturally not executed for test methods that are not configured to run within a transaction.

任何之前的方法(例如使用JUnit4@Before修饰的方法)和任何之后的方法(例如使用JUnit4@After修饰的方法)将在事务中执行。此外,使用@BeforeTransaction@AfterTransaction的方法如果测试方法不在事务中则不会执行。

 

Configuring a transaction manager

配置事务管理器

 

TransactionalTestExecutionListener expects a PlatformTransactionManager bean to be defined in the Spring ApplicationContext for the test. In case there are multiple instances of PlatformTransactionManager within the tests ApplicationContext, a qualifier may be declared via @Transactional("myTxMgr") or @Transactional(transactionManager = "myTxMgr"), or TransactionManagementConfigurer can be implemented by an @Configuration class. Consult the javadocs for TestContextTransactionUtils.retrieveTransactionManager() for details on the algorithm used to look up a transaction manager in the tests ApplicationContext.

TransactionalTestExecutionListener期望PlatformTransactionManagerbean被定义在springApplicationContext中对于测试。因此这些PlatformTransactionManager的多实例在测试的ApplicationContext中,通过@Transactional("myTxMgr")@Transactional(transactionManager = "myTxMgr")TransactionManagementConfigurer定义可以实现通过@Configuration类。查阅TestContextTransactionUtils.retrieveTransactionManager()javadocs来了解相应算法的使用对于事务管理在测试ApplicationContext中。

 

Demonstration of all transaction-related annotations

所有事务相关的注解示范

 

The following JUnit 4 based example displays a fictitious integration testing scenario highlighting all transaction-related annotations. The example is not intended to demonstrate best practices but rather to demonstrate how these annotations can be used. Consult the annotation support section for further information and configuration examples. Transaction management for @Sql contains an additional example using @Sql for declarative SQL script execution with default transaction rollback semantics.

下面基于JUnit4的例子展示了虚构的集成测试环境突出的所有事务相关的注解。这个例子并不是最好的例子但是描述了如何使用这些注解。查阅更多的信息有关注解的支持。事务管理对于@Sql包含一个额外的例子使用@Sql为了定义SQL脚本执行对于默认的事务回滚语义。

 

@RunWith(SpringRunner.class)

@ContextConfiguration

@Transactional(transactionManager = "txMgr")

@Commit

public class FictitiousTransactionalTest {

 

    @BeforeTransaction

    void verifyInitialDatabaseState() {

        // logic to verify the initial state before a transaction is started

    }

 

    @Before

    public void setUpTestDataWithinTransaction() {

        // set up test data within the transaction

    }

 

    @Test

    // overrides the class-level @Commit setting

    @Rollback

    public void modifyDatabaseWithinTransaction() {

        // logic which uses the test data and modifies database state

    }

 

    @After

    public void tearDownWithinTransaction() {

        // execute "tear down" logic within the transaction

    }

 

    @AfterTransaction

    void verifyFinalDatabaseState() {

        // logic to verify the final state after transaction has rolled back

    }

 

}

 

[Note]

注意

 

When you test application code that manipulates the state of a Hibernate session or JPA persistence context, make sure to flush the underlying unit of work within test methods that execute that code. Failing to flush the underlying unit of work can produce false positives: your test may pass, but the same code throws an exception in a live, production environment. In the following Hibernate-based example test case, one method demonstrates a false positive, and the other method correctly exposes the results of flushing the session. Note that this applies to any ORM frameworks that maintain an in-memory unit of work.

当你的测试应用代码操纵Hibernate会话或JPA持久化上下文的状态,保证刷新底层的单元在测试方法中来执行代码。刷新底层单元失败可以出现错误:你的测试会通过,但是先攻的代码会抛出异常在生产或实际环境中。下面基于Hibernate的测试实例,一个测试方法描述的作为的案例并且其他方法正确的展示的结果由于刷新的会话。注意这适用于任何的ORM框架对于内存的工作单元。

 

// ...

 

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

}

 

// ...

 

Or for 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();

}

 

// ...

 

15.5.8 Executing SQL scripts

执行SQL脚本

 

When writing integration tests against a relational database, it is often beneficial to execute SQL scripts to modify the database schema or insert test data into tables. The spring-jdbc module provides support for initializing an embedded or existing database by executing SQL scripts when the Spring ApplicationContext is loaded. See Section 19.8, Embedded database supportand Section 19.8.5, Testing data access logic with an embedded databasefor details.

当使用关系数据库实现集成测试时,使用sql脚本来修改数据库schema或向表中插入测试数据是有意义的。spring-jdbc模块提供了对内置或已存的数据库的支持通过执行sql脚本当springApplicationContext被加载的时候。将19.8节,“嵌入数据库的支持”和19.8.5节“测试数据访问逻辑和嵌入数据库”

 

Although it is very useful to initialize a database for testing once when the ApplicationContext is loaded, sometimes it is essential to be able to modify the database during integration tests. The following sections explain how to execute SQL scripts programmatically and declaratively during integration tests.

尽管对于实例化数据库十分有用对于测试当ApplicationContext被加载的时候,有时需要修改数据库在集成测试中。下面的章节解释了如何编程执行sql脚本在集成测试中。

 

Executing SQL scripts programmatically

编程执行sql脚本

 

Spring provides the following options for executing SQL scripts programmatically within integration test methods.

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 provides a collection of static utility methods for working with SQL scripts and is mainly intended for internal use within the framework. However, if you require full control over how SQL scripts are parsed and executed, ScriptUtils may suit your needs better than some of the other alternatives described below. Consult the javadocs for individual methods in ScriptUtils for further details.

ScriptUtils提供了一个静态工具方法的集合对于sql脚本并且主要用于框架内部的使用。然而,如果你需要完全控制sql脚本的解析和执行,ScriptUtils可能适合你的需要比其他的一些描述如下。参考ScriptUtilsjavadocs和独立的方法来了解更多信息。

 

ResourceDatabasePopulator provides a simple object-based API for programmatically populating, initializing, or cleaning up a database using SQL scripts defined in external resources. ResourceDatabasePopulator provides options for configuring the character encoding, statement separator, comment delimiters, and error handling flags used when parsing and executing the scripts, and each of the configuration options has a reasonable default value. Consult the javadocs for details on default values. To execute the scripts configured in a ResourceDatabasePopulator, you can invoke either the populate(Connection) method to execute the populator against a java.sql.Connection or the execute(DataSource) method to execute the populator against a javax.sql.DataSource. The following example specifies SQL scripts for a test schema and test data, sets the statement separator to "@@", and then executes the scripts against a DataSource.

ResourceDatabasePopulator提供了一个简单的基于objectAPI用于编程初始化或清理数据库通过sql脚本定义在外部资源中。ResourceDatabasePopulator提供了选项来配置编码格式、语句分隔符、注释分隔符和错误处理标识用于解析和执行脚本并且每个配置选项有合理的默认值。对于默认值请参考javadocs。为了在ResourceDatabasePopulator中执行配置的sql脚本,你可以调用populate方法来执行通过java.sql.Connectionexecute方法来执行通过javax.sql.DataSource。下面的例子指定了sql脚本用于测试schema和测试数据,设置声明分隔符为@@并且通过DataSource来执行脚本。

 

@Test

public 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

}

 

Note that ResourceDatabasePopulator internally delegates to ScriptUtils for parsing and executing SQL scripts. Similarly, the executeSqlScript(..) methods in AbstractTransactionalJUnit4SpringContextTests and AbstractTransactionalTestNGSpringContextTests internally use a ResourceDatabasePopulator for executing SQL scripts. Consult the javadocs for the various executeSqlScript(..) methods for further details.

注意ResourceDatabasePopulator内部是委托给ScriptUtils来解析和执行sql脚本的。相似的,executeSqlScript方法在AbstractTransactionalJUnit4SpringContextTestsAbstractTransactionalTestNGSpringContextTests中使用了ResourceDatabasePopulator用于执行sql脚本。参考不同的executeSqlScript方法的javadocs来了解更多的信息。

 

Executing SQL scripts declaratively with @Sql

使用@Sql来执行sql脚本

 

In addition to the aforementioned mechanisms for executing SQL scripts programmatically, SQL scripts can also be configured declaratively in the Spring TestContext Framework. Specifically, the @Sql annotation can be declared on a test class or test method to configure the resource paths to SQL scripts that should be executed against a given database either before or after an integration test method. Note that method-level declarations override class-level declarations and that support for @Sql is provided by the SqlScriptsTestExecutionListener which is enabled by default.

此外除了上面提到的编程执行sql脚本外,sql也可以直接配置在springTestContext框架中。特定的,@Sql注解可以修饰一个测试类或测试方法来配置sql脚本的资源路径应当被执行对于给定的数据库或在集成测试方法之前或之后。注意方法级别的定义会覆盖类级别的定义对于@Sql的支持,默认是通过SqlScriptsTestExecutionListener来提供实现的。

 

Path resource semantics

路径资源语义

 

Each path will be interpreted as a Spring Resource. A plain path — for example, "schema.sql" — will be treated as a classpath resource that is relative to the package in which the test class is defined. A path starting with a slash will be treated as an absolute classpath resource, for example: "/org/example/schema.sql". A path which references a URL (e.g., a path prefixed with classpath:, file:, http:, etc.) will be loaded using the specified resource protocol.

每个路径将被理解为一个spring资源。一个普通的路径————例如,“schema.sql”————将被处理作为一个classpath路径依赖于测试类定义的包的位置。一个路径开始于斜线将被认为是绝对路径资源,例如:"/org/example/schema.sql"。一个路径引用一个URL(例如,一个有前缀的路径classpathfilehttp等等)将被加载通过特定的协议。

 

The following example demonstrates how to use @Sql at the class level and at the method level within a JUnit 4 based integration test class.

下面的例子描述了@Sql的使用在类级别和方法级别配合JUnit4的集成测试类。

 

@RunWith(SpringRunner.class)

@ContextConfiguration

@Sql("/test-schema.sql")

public class DatabaseTests {

 

    @Test

    public void emptySchemaTest {

        // execute code that uses the test schema without any test data

    }

 

    @Test

    @Sql({"/test-schema.sql", "/test-user-data.sql"})

    public void userTest {

        // execute code that uses the test schema and test data

    }

}

 

Default script detection

探测默认的脚本

 

If no SQL scripts are specified, an attempt will be made to detect a default script depending on where @Sql is declared. If a default cannot be detected, an IllegalStateException will be thrown.

如果没有sql脚本被执行,将会尝试获取默认的脚本依赖于定义的@Sql。如果默认的脚本没有被找到将会抛出IllegalStateException异常。

 

    class-level declaration: if the annotated test class is com.example.MyTest, the corresponding default script is "classpath:com/example/MyTest.sql".

类级别定义:如果注解了一个测试类是com.example.MyTest,那么相应的脚本是"classpath:com/example/MyTest.sql"

    method-level declaration: if the annotated test method is named testMethod() and is defined in the class com.example.MyTest, the corresponding default script is "classpath:com/example/MyTest.testMethod.sql".

方法级别定义:如果注解了一个测试方法名字为testMethod并且定义在com.example.MyTest类中,那么相应的默认脚本是"classpath:com/example/MyTest.testMethod.sql"

 

Declaring multiple @Sql sets

定义多个@Sql集合

 

If multiple sets of SQL scripts need to be configured for a given test class or test method but with different syntax configuration, different error handling rules, or different execution phases per set, it is possible to declare multiple instances of @Sql. With Java 8, @Sql can be used as a repeatable annotation. Otherwise, the @SqlGroup annotation can be used as an explicit container for declaring multiple instances of @Sql.

如果多个sql脚本的集合需要被配置对于给定的测试类或测试方法但是有不同的语法配置、不同错误处理规则或不同的执行语句对于每个集合,定义多个@Sql实例是有意义的。在Java8中,@Sql可以被用于作为一个可重复的注解。此外,@SqlGroup注解可以被使用作为一个明确的容器用于定义多个@Sql的实例。

 

The following example demonstrates the use of @Sql as a repeatable annotation using Java 8. In this scenario the test-schema.sql script uses a different syntax for single-line comments.

下面的例子展示了@Sql作为一个重复使用的注解在Java8中的应用。在这个场景中test-schema.sql脚本被使用一个不同的语法对于单行注释。

 

@Test

@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

}

 

The following example is identical to the above except that the @Sql declarations are grouped together within @SqlGroup for compatibility with Java 6 and Java 7.

下面的例子与上面使用@Sql定义的是一样的并且配合了@SqlGroup兼容Java6Java7.

 

@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

}

 

Script execution phases

脚本执行阶段

 

By default, SQL scripts will be executed before the corresponding test method. However, if a particular set of scripts needs to be executed after the test method — for example, to clean up database state — the executionPhase attribute in @Sql can be used as seen in the following example. Note that ISOLATED and AFTER_TEST_METHOD are statically imported from Sql.TransactionMode and Sql.ExecutionPhase respectively.

默认情况下,sql脚本将被执行在相应的测试方法之前。然而,如果一个特定的脚本集合需要被执行在方法之后————例如,为了清理数据库的状态————@Sql中的executionPhase属性可以被使用如下面的例子。注意ISOLATEDAFTER_TEST_METHODSql.TransactionModeheSql.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

)

public void userTest {

    // execute code that needs the test data to be committed

    // to the database outside of the test's transaction

}

 

Script configuration with @SqlConfig

使用@SqlConfig的脚本配置

 

Configuration for script parsing and error handling can be configured via the @SqlConfig annotation. When declared as a class-level annotation on an integration test class, @SqlConfig serves as global configuration for all SQL scripts within the test class hierarchy. When declared directly via the config attribute of the @Sql annotation, @SqlConfig serves as local configuration for the SQL scripts declared within the enclosing @Sql annotation. Every attribute in @SqlConfig has an implicit default value which is documented in the javadocs of the corresponding attribute. Due to the rules defined for annotation attributes in the Java Language Specification, it is unfortunately not possible to assign a value of null to an annotation attribute. Thus, in order to support overrides of inherited global configuration, @SqlConfig attributes have an explicit default value of either "" for Strings or DEFAULT for Enums. This approach allows local declarations of @SqlConfig to selectively override individual attributes from global declarations of @SqlConfig by providing a value other than "" or DEFAULT. Global @SqlConfig attributes are inherited whenever local @SqlConfig attributes do not supply an explicit value other than "" or DEFAULT. Explicit local configuration therefore overrides global configuration.

对于脚本解析和错误处理可以被配置通过@SqlConfig注解。当定义一个类级别注解对于集成测试类时。@SqlConfig作为一个全局的配置对于测试类结构中的类都有效。当直接定义在@Sql注解中时,@SqlConfig将服务于本地的sql脚本定义在@Sql注解的修饰范围中。每个@SqlConfig中的属性都有默认值并描述在javadocs中。由于规则对于注解属性的定义在java语言规范中,不能声明null对于注解属性。因此支持覆盖全局配置,@SqlConfig属性有默认的值“”对于字符串,如果是枚举则是DEFAULT。这个方法允许@SqlConfig的本地定义来选择覆盖独立的属性来自全局的@SqlConfig定义通过默认值“”或DEFAULT来提供的。全局的@SqlConfig被继承当本地的@SqlConfig属性没有明确提供值时。明确的本地配置会覆盖全局配置。

 

The configuration options provided by @Sql and @SqlConfig are equivalent to those supported by ScriptUtils and ResourceDatabasePopulator but are a superset of those provided by the XML namespace element. Consult the javadocs of individual attributes in @Sql and @SqlConfig for details.

@Sql提供的配置选项和@SqlConfig是相同的对于ScriptUtilsResourceDatabasePopulator的支持也是xml命名空间支持的超集。详见@Sql@SqlConfigjavadocs

 

Transaction management for @Sql

使用@Sql的事务管理

 

By default, the SqlScriptsTestExecutionListener will infer the desired transaction semantics for scripts configured via @Sql. Specifically, SQL scripts will be executed without a transaction, within an existing Spring-managed transaction — for example, a transaction managed by the TransactionalTestExecutionListener for a test annotated with @Transactional — or within an isolated transaction, depending on the configured value of the transactionMode attribute in @SqlConfig and the presence of a PlatformTransactionManager in the tests ApplicationContext. As a bare minimum however, a javax.sql.DataSource must be present in the tests ApplicationContext.

默认情况下,SqlScriptsTestExecutionListener将会通过@Sql来推断事务的语义。特定的,sql脚本将被执行在事务之外,在已有的spring管理的事务中执行————例如,TransactionalTestExecutionListener管理的事务用于测试类使用了@Transactional注解————或独立的注解,依赖于@SqlConfigtransactionMode配置的值和测试ApplicationContext中的PlatformTransactionManager。作为一个绝对最小值,javax.sql.DataSource必须定义在测试的ApplicationContext中。

 

If the algorithms used by SqlScriptsTestExecutionListener to detect a DataSource and PlatformTransactionManager and infer the transaction semantics do not suit your needs, you may specify explicit names via the dataSource and transactionManager attributes of @SqlConfig. Furthermore, the transaction propagation behavior can be controlled via the transactionMode attribute of @SqlConfig — for example, if scripts should be executed in an isolated transaction. Although a thorough discussion of all supported options for transaction management with @Sql is beyond the scope of this reference manual, the javadocs for @SqlConfig and SqlScriptsTestExecutionListener provide detailed information, and the following example demonstrates a typical testing scenario using JUnit 4 and transactional tests with @Sql. Note that there is no need to clean up the database after the usersTest() method is executed since any changes made to the database (either within the test method or within the /test-data.sql script) will be automatically rolled back by the TransactionalTestExecutionListener (see transaction management for details).

如果SqlScriptsTestExecutionListener使用的算法来探测DataSourcePlatformTransactionManager并且推断事务语义不会适应你的需要,你可以明确的名字通过@SqlConfig中的dataSourcetransactionManager属性。此外,事务传播行为可以通过@SqlConfigtransactionMode值来控制————例如,如果脚本应当被执行在一个孤立的事务中。尽管@Sql中所有事务管理的支持选项取决于这个参考文档,@SqlConfigSqlScriptsTestExecutionListenerjavadocs提供了更加详尽的信息,并且下面的例子描述了一个典型的测试场景使用JUnit4@Sql的事务测试。注意不需要清理数据库在usersTest方法之后因为任何对于数据库的改变(包括在测试方法或在test-data.sql脚本中)将会自动回滚通过TransactionalTestExecutionListener来执行(详见事务管理)

 

@RunWith(SpringRunner.class)

@ContextConfiguration(classes = TestDatabaseConfig.class)

@Transactional

public class TransactionalSqlScriptsTests {

 

    protected JdbcTemplate jdbcTemplate;

 

    @Autowired

    public void setDataSource(DataSource dataSource) {

        this.jdbcTemplate = new JdbcTemplate(dataSource);

    }

 

    @Test

    @Sql("/test-data.sql")

    public void usersTest() {

        // verify state in test database:

        assertNumUsers(2);

        // execute code that uses the test data...

    }

 

    protected int countRowsInTable(String tableName) {

        return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);

    }

 

    protected void assertNumUsers(int expected) {

        assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));

    }

}

 

15.5.9 TestContext Framework support classes

Test框架支持类

 

Spring JUnit 4 Runner

 

The Spring TestContext Framework offers full integration with JUnit 4 through a custom runner (supported on JUnit 4.12 or higher). By annotating test classes with @RunWith(SpringJUnit4ClassRunner.class) or the shorter @RunWith(SpringRunner.class) variant, developers can implement standard JUnit 4 based unit and integration tests and simultaneously reap the benefits of the TestContext framework such as support for loading application contexts, dependency injection of test instances, transactional test method execution, and so on. If you would like to use the Spring TestContext Framework with an alternative runner such as JUnit 4s Parameterized or third-party runners such as the MockitoJUnitRunner, you may optionally use Springs support for JUnit rules instead.

springTestContext框架提供了全面的集成对于JUnit4通过一个自定义runner(支持JUnit4.12及以上版本)。通过使用@RunWith注解测试类或@RunWith变量,开发者可以实现标准的JUnit4基本单元和集成测试并同时从测试框架中获益例如对于应用上下文的加载,测试夹具的依赖注入,事务测试方法执行等等。如果你希望使用springTestContext框架配合相应的runner例如JUnit4的参数化或第三方的runner例如MockitoJUnitRunner,你可以使用spring的支持对于JUnit的规则。

 

The following code listing displays the minimal requirements for configuring a test class to run with the custom Spring Runner. @TestExecutionListeners is configured with an empty list in order to disable the default listeners, which otherwise would require an ApplicationContext to be configured through @ContextConfiguration.

下面的代码展示了最小需求对于配置一个测试类使用自定义的springRunner@TestExecutionListeners被配置了一个空的列表用于取消默认的监听器,否则将会需要@ContextConfiguration配置的ApplicationContext

 

@RunWith(SpringRunner.class)

@TestExecutionListeners({})

public class SimpleTest {

 

   @Test

   public void testMethod() {

      // execute test logic...

   }

}

 

Spring JUnit 4 Rules

 

The org.springframework.test.context.junit4.rules package provides the following JUnit 4 rules (supported on JUnit 4.12 or higher).

org.springframework.test.context.junit4.rules包提供了下面的JUnit4规则(JUnit4.12及以上的版本支持)

 

    SpringClassRule

    SpringMethodRule

 

SpringClassRule is a JUnit TestRule that supports class-level features of the Spring TestContext Framework; whereas, SpringMethodRule is a JUnit MethodRule that supports instance-level and method-level features of the Spring TestContext Framework.

SpringClassRule是一个JUnitTestRule支持类级别的特性对于SpringTestContext框架,然而,SpringMethodRule是一个JUnitMethodRule支持实例级别和方法级别的特性对于springTestContext框架。

 

In contrast to the SpringRunner, Springs rule-based JUnit support has the advantage that it is independent of any org.junit.runner.Runner implementation and can therefore be combined with existing alternative runners like JUnit 4s Parameterized or third-party runners such as the MockitoJUnitRunner.

SpringRunner形成对比,spring基于规则的JUnit支持有一定的优势对于任何org.junit.runner.Runner的实现独立并且可以合并已有的runners例如JUnit4的参数化或第三方框架的runner例如MockitoJUnitRunner

 

In order to support the full functionality of the TestContext framework, a SpringClassRule must be combined with a SpringMethodRule. The following example demonstrates the proper way to declare these rules in an integration test.

为了支持TestContext框架的全部功能,SpringClassRule必须被合并SpringMethodRule。下面的例子展示了合适的方式对于声明这些规则在集成测试中。

 

// Optionally specify a non-Spring Runner via @RunWith(...)

@ContextConfiguration

public class IntegrationTest {

 

   @ClassRule

   public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();

 

   @Rule

   public final SpringMethodRule springMethodRule = new SpringMethodRule();

 

   @Test

   public void testMethod() {

      // execute test logic...

   }

}

 

JUnit 4 support classes

JUnit4的支持类

 

The org.springframework.test.context.junit4 package provides the following support classes for JUnit 4 based test cases (supported on JUnit 4.12 or higher).

org.springframework.test.context.junit4包中提供了下面的支持类对于JUnit4的基本测试(Junit4.12及以上版本支持)

 

    AbstractJUnit4SpringContextTests

    AbstractTransactionalJUnit4SpringContextTests

 

AbstractJUnit4SpringContextTests is an abstract base test class that integrates the Spring TestContext Framework with explicit ApplicationContext testing support in a JUnit 4 environment. When you extend AbstractJUnit4SpringContextTests, you can access a protected applicationContext instance variable that can be used to perform explicit bean lookups or to test the state of the context as a whole.

AbstractJUnit4SpringContextTests是一个抽象基本测试类集合springTestContext框架对于在JUnit4环境中的ApplicationContext测试支持。当你继承AbstractJUnit4SpringContextTests,你可以访问受保护的ApplicationContext实例变量可以被使用用于处理特定bean的查找或测试上下文的状态。

 

AbstractTransactionalJUnit4SpringContextTests is an abstract transactional extension of AbstractJUnit4SpringContextTests that adds some convenience functionality for JDBC access. This class expects a javax.sql.DataSource bean and a PlatformTransactionManager bean to be defined in the ApplicationContext. When you extend AbstractTransactionalJUnit4SpringContextTests you can access a protected jdbcTemplate instance variable that can be used to execute SQL statements to query the database. Such queries can be used to confirm database state both prior to and after execution of database-related application code, and Spring ensures that such queries run in the scope of the same transaction as the application code. When used in conjunction with an ORM tool, be sure to avoid false positives. As mentioned in Section 15.3, JDBC Testing Support, AbstractTransactionalJUnit4SpringContextTests also provides convenience methods which delegate to methods in JdbcTestUtils using the aforementioned jdbcTemplate. Furthermore, AbstractTransactionalJUnit4SpringContextTests provides an executeSqlScript(..) method for executing SQL scripts against the configured DataSource.

AbstractTransactionalJUnit4SpringContextTests是一个抽象AbstractJUnit4SpringContextTests的事务扩展添加了一个方便的功能用于JDBC访问。这个类需要javax.sql.DataSourcebeanPlatformTransactionManagerbean被定义在ApplicationContext中。当你继承AbstractTransactionalJUnit4SpringContextTests你可以访问受保护的jdbcTemplate实例变量可以用于执行sql语句来查询数据库。这样的查询可以被用于确认数据库状态在相应的代码执行的前后,并且spring保证这样的查询在相同的事务范围执行作为应用代码。当配合ORM工具使用的时候,保证避免错误的使用。在15.3节中提到的,“JDBC测试支持”,AbstractTransactionalJUnit4SpringContextTests也提供方便的方法委托给JdbcTestUtils中的方法使用上述的jdbcTemplate。此外,AbstractTransactionalJUnit4SpringContextTests提供了executeSqlScript方法用于执行sql脚本来配置数据库。

 

[Tip]

提示

 

These classes are a convenience for extension. If you do not want your test classes to be tied to a Spring-specific class hierarchy, you can configure your own custom test classes by using @RunWith(SpringRunner.class) or Springs JUnit rules.

这些类可以方便扩展。如果你不希望你的测试类绑定于特定的spring的类结构,你可以配置你自定义的测试类通过使用@RunWithspringJUnit规则。

 

TestNG support classes

TestNG的支持类

 

The org.springframework.test.context.testng package provides the following support classes for TestNG based test cases.

org.springframework.test.context.testng package包提供了下面的支持类用于基于TestNG的测试类。

 

    AbstractTestNGSpringContextTests

    AbstractTransactionalTestNGSpringContextTests

 

AbstractTestNGSpringContextTests is an abstract base test class that integrates the Spring TestContext Framework with explicit ApplicationContext testing support in a TestNG environment. When you extend AbstractTestNGSpringContextTests, you can access a protected applicationContext instance variable that can be used to perform explicit bean lookups or to test the state of the context as a whole.

AbstractTestNGSpringContextTests是一个抽象测试基类在springTestContext框架中用于在TestNG环境中的ApplicationContext测试支持。当你继承AbstractTestNGSpringContextTests,你可以访问受保护的应用上下文实例变量可以被用于查找bean或测试上下文状态。

 

AbstractTransactionalTestNGSpringContextTests is an abstract transactional extension of AbstractTestNGSpringContextTests that adds some convenience functionality for JDBC access. This class expects a javax.sql.DataSource bean and a PlatformTransactionManager bean to be defined in the ApplicationContext. When you extend AbstractTransactionalTestNGSpringContextTests you can access a protected jdbcTemplate instance variable that can be used to execute SQL statements to query the database. Such queries can be used to confirm database state both prior to and after execution of database-related application code, and Spring ensures that such queries run in the scope of the same transaction as the application code. When used in conjunction with an ORM tool, be sure to avoid false positives. As mentioned in Section 15.3, JDBC Testing Support, AbstractTransactionalTestNGSpringContextTests also provides convenience methods which delegate to methods in JdbcTestUtils using the aforementioned jdbcTemplate. Furthermore, AbstractTransactionalTestNGSpringContextTests provides an executeSqlScript(..) method for executing SQL scripts against the configured DataSource.

AbstractTransactionalTestNGSpringContextTestsAbstractTestNGSpringContextTests的一个抽象事务扩展添加了一个方便的方法用于JDBC访问。这个类需要一个javax.sql.DataSourcebean和一个PlatformTransactionManagerbean定义在ApplicationContext中。当你继承AbstractTransactionalTestNGSpringContextTests你可以访问受保护的jdbcTemplate实例变量可以用于执行SQL语句来查询数据库。这样的查询可以被用于确认数据库状态在执行数据库相关的代码的前后,并且spring可以保证这些查询在一些应用代码事务的范围内运行。当配合ORM工具使用的时候,保证避免positives。在15.3节中提到的“JDBC测试支持”,AbstractTransactionalTestNGSpringContextTests也提供了方便的方法委托给了JdbcTestUtils中的方法使用了上面提到的jdbcTemplete。此外,AbstractTransactionalTestNGSpringContextTests提供了一个executeSqlScript方法用于执行SQL脚本对于给定的数据库。

 

[Tip]

提示

 

These classes are a convenience for extension. If you do not want your test classes to be tied to a Spring-specific class hierarchy, you can configure your own custom test classes by using @ContextConfiguration, @TestExecutionListeners, and so on, and by manually instrumenting your test class with a TestContextManager. See the source code of AbstractTestNGSpringContextTests for an example of how to instrument your test class.

这些类都是很方便的扩展。如果你不需要测试类和spring特定的类结构绑定,你可以配置你自己的测试通过使用@ContextConfiguration@TestExecutionListeners等等注解,并且通过手动检测你的测试类通过TestContextManager。见AbstractTestNGSpringContextTests的源代码和其相关的例子。

 

15.6 Spring MVC Test Framework

 

The Spring MVC Test framework provides first class support for testing Spring MVC code using a fluent API that can be used with JUnit, TestNG, or any other testing framework. Its built on the Servlet API mock objects from the spring-test module and hence does not use a running Servlet container. It uses the DispatcherServlet to provide full Spring MVC runtime behavior and provides support for loading actual Spring configuration with the TestContext framework in addition to a standalone mode in which controllers may be instantiated manually and tested one at a time.

springmvc测试框架提供了支持对于测试springmvc代码使用丰富的API可以配置通过JUnitTestNG或其他测试框架。他们的构建基于ServletAPI模拟来自spring-test模块并且不需要在Servlet容器上运行。他使用DispatcherServlet来支持全部的SpringMVC运行时行为并提供支持用于加载实际的spring的配置通过TestContext框架以额外的模式使得控制器可以手动实例化在测试的时候。

 

Spring MVC Test also provides client-side support for testing code that uses the RestTemplate. Client-side tests mock the server responses and also do not use a running server.

springMVC测试也提供了客户端支持用于测试代码通过使用RestTemplate。客户端测试模拟服务器响应并且不需要在服务器上运行。

 

[Tip]

提示

 

Spring Boot provides an option to write full, end-to-end integration tests that include a running server. If this is your goal please have a look at the Spring Boot reference page. For more information on the differences between out-of-container and end-to-end integration tests, see the section called Differences between Out-of-Container and End-to-End Integration Tests.

springboot提供了一个选项用于书写全面、点到点的集成测试包括一个运行的服务器。如果这是你的目标请查看springboot的参考页。对于更多的信息有关容器外和点对点集成测试的不同,请参考相关的章节名为“容器外和点对点集成测试的不同点”。

 

15.6.1 Server-Side Tests

服务器方面的测试

 

Its easy to write a plain unit test for a Spring MVC controller using JUnit or TestNG: simply instantiate the controller, inject it with mocked or stubbed dependencies, and call its methods passing MockHttpServletRequest, MockHttpServletResponse, etc., as necessary. However, when writing such a unit test, much remains untested: for example, request mappings, data binding, type conversion, validation, and much more. Furthermore, other controller methods such as @InitBinder, @ModelAttribute, and @ExceptionHandler may also be invoked as part of the request processing lifecycle.

可以简单书写一个普通的单元测试对于springmvc控制器通过使用JUnitTestNG:简单实例化控制器,注入模拟或其他依赖并且调用他的方法通过MockHttpServletRequestMockHttpServletResponse等等,根据需要。然而当书写这样的单元测试时,很多会被没有测试,例如,请求mapping、数据绑定、类型转换等等。此外,其他控制器方法例如@InitBinder@ModelAttribute@ExceptionHandler可以被调用作为请求处理过程的一部分。

 

The goal of Spring MVC Test is to provide an effective way for testing controllers by performing requests and generating responses through the actual DispatcherServlet.

springmvc测试用于提供一个有效的方法用于测试控制器通过执行请求和生成响应通过实际的DispatcherServlet

 

Spring MVC Test builds on the familiar "mock" implementations of the Servlet API available in the spring-test module. This allows performing requests and generating responses without the need for running in a Servlet container. For the most part everything should work as it does at runtime with a few notable exceptions as explained in the section called Differences between Out-of-Container and End-to-End Integration Tests. Here is a JUnit 4 based example of using Spring MVC Test:

springmvc测试构建了类似的servletapi模拟实现在spring-test模块中。这允许实现请求和生成响应不需要运行在一个servlet容器中。对于大多数的情况应当在运行时并且配合一些冥想异常的情况会在章节“容器外和点对点集成测试的不同”中说明。下面是一个基于Junt4的基本案例配合使用springmvc测试:

 

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

 

@RunWith(SpringRunner.class)

@WebAppConfiguration

@ContextConfiguration("test-servlet-context.xml")

public class ExampleTests {

 

    @Autowired

    private WebApplicationContext wac;

 

    private MockMvc mockMvc;

 

    @Before

    public void setup() {

        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();

    }

 

    @Test

    public void getAccount() throws Exception {

        this.mockMvc.perform(get("/accounts/1").accept(MediaType.parseMediaType("application/json;charset=UTF-8")))

            .andExpect(status().isOk())

            .andExpect(content().contentType("application/json"))

            .andExpect(jsonPath("$.name").value("Lee"));

    }

 

}

 

The above test relies on the WebApplicationContext support of the TestContext framework for loading Spring configuration from an XML configuration file located in the same package as the test class, but Java-based and Groovy-based configuration are also supported. See these sample tests.

上面的测试依赖于WebApplicationContext的支持对于TestContext框架用于加载spring的配置来自xml配置文件在相应的测试类的同一个包中,但是基于java和基于groovy的配置也是支持的。见这些测试案例。

 

The MockMvc instance is used to perform a GET request to "/accounts/1" and verify that the resulting response has status 200, the content type is "application/json", and the response body has a JSON property called "name" with the value "Lee". The jsonPath syntax is supported through the Jayway JsonPath project. There are lots of other options for verifying the result of the performed request that will be discussed below.

MockMvc实例被用于处理GET请求对于"/accounts/1"并且验证响应状态码200,内容类型是"application/json"并且响应体有一个JSON属性名字为name并且值是LeejsonPath的语法支持通过JaywayJsonPath项目。有许多其他的选项用于验证请求的结果将会在后续讨论。

 

Static Imports

静态导入

 

The fluent API in the example above requires a few static imports such as MockMvcRequestBuilders.*, MockMvcResultMatchers.*, and MockMvcBuilders.*. An easy way to find these classes is to search for types matching "MockMvc*". If using Eclipse, be sure to add them as "favorite static members" in the Eclipse preferences under Java Editor Content Assist Favorites. That will allow use of content assist after typing the first character of the static method name. Other IDEs (e.g. IntelliJ) may not require any additional configuration. Just check the support for code completion on static members.

丰富的API在上面的例子中要求一些静态导入例如MockMvcRequestBuilders.*MockMvcResultMatchers.*MockMvcBuilders.*。一个简单的方法来发现这些类使用通过类型匹配"MockMvc*"。如果使用Eclipse,保证将这些添加到"favorite static members"Eclipse中通过preferences下的Java Editor Content Assist Favorites。这将会允许使用内容辅助在我们敲入第一个静态方法字母后。其他的IDE(例如IntellJ)可能不需要额外的配置。只要检查是否支持静态成员的代码完成就可以。

 

Setup Options

设置选项

 

There are two main options for creating an instance of MockMvc. The first is to load Spring MVC configuration through the TestContext framework, which loads the Spring configuration and injects a WebApplicationContext into the test to use to build a MockMvc instance:

有两个主要的选项用于创建MockMvc的实例。第一个是加载springmvc配置通过TestContext框架,通过spring的配置加载和注入WebApplicationContext到测试中对于测试用于构建一个MockMvc实例。

 

@RunWith(SpringRunner.class)

@WebAppConfiguration

@ContextConfiguration("my-servlet-context.xml")

public class MyWebTests {

 

    @Autowired

    private WebApplicationContext wac;

 

    private MockMvc mockMvc;

 

    @Before

    public void setup() {

        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();

    }

 

    // ...

 

}

 

The second is to simply create a controller instance manually without loading Spring configuration. Instead basic default configuration, roughly comparable to that of the MVC JavaConfig or the MVC namespace, is automatically created and can be customized to a degree:

第二就是简单创建一个控制器实例不需要加载spring的配置。代替基本的默认配置,简单的比较MCVJavaConfigMVC命名空间,可以自动创建和可以被自定义在一定程度:

 

public class MyWebTests {

 

    private MockMvc mockMvc;

 

    @Before

    public void setup() {

        this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();

    }

 

    // ...

 

}

 

Which setup option should you use?

应该使用什么样的设置选项。

 

The "webAppContextSetup" loads your actual Spring MVC configuration resulting in a more complete integration test. Since the TestContext framework caches the loaded Spring configuration, it helps keep tests running fast, even as you introduce more tests in your test suite. Furthermore, you can inject mock services into controllers through Spring configuration in order to remain focused on testing the web layer. Here is an example of declaring a mock service with Mockito:

"webAppContextSetup"加载你实际的springmvc配置结果在一个更加完整的集成测试中。自从TestContext框架缓存加载的spring配置,他保证测试运行的更快,即使你引入更多的测试在你的测试夹具中。此外,你可以注入模拟服务到控制器通过spring配置以便于保留对于测试web层的关注。下面是一个例子有关定义一个模拟服务使用Mockito

 

    

 

You can then inject the mock service into the test in order set up and verify expectations:

你可以注入模拟服务到测试中用于设置和验证期望值:

 

@RunWith(SpringRunner.class)

@WebAppConfiguration

@ContextConfiguration("test-servlet-context.xml")

public class AccountTests {

 

    @Autowired

    private WebApplicationContext wac;

 

    private MockMvc mockMvc;

 

    @Autowired

    private AccountService accountService;

 

    // ...

 

}

 

The "standaloneSetup" on the other hand is a little closer to a unit test. It tests one controller at a time: the controller can be injected with mock dependencies manually, and it doesnt involve loading Spring configuration. Such tests are more focused on style and make it easier to see which controller is being tested, whether any specific Spring MVC configuration is required to work, and so on. The "standaloneSetup" is also a very convenient way to write ad-hoc tests to verify specific behavior or to debug an issue.

"standaloneSetup"在另一方面是一个小的近亲对于单元测试。他一次测试一个控制器:控制器可以被注入使用手动模拟依赖,并且他不需要调用加载spirng的配置。这样的测试可以更加关注于风格和使得控制器更加简单被测试,并且特定的spirngmvc配置会被需要等等。"standaloneSetup"也是一个种很方便的方式用于书写ad-hoc测试用于验证特定的行为或调试问题。

 

Just like with any "integration vs. unit testing" debate, there is no right or wrong answer. However, using the "standaloneSetup" does imply the need for additional "webAppContextSetup" tests in order to verify your Spring MVC configuration. Alternatively, you may choose to write all tests with "webAppContextSetup" in order to always test against your actual Spring MVC configuration.

就像“集成测试vs单元测试”的辩论,是不存在对和错的。然而使用"standaloneSetup"需要额外的"webAppContextSetup"测试用于验证springmvc配置。作为替代,你可以选择书写所有的测试配合"webAppContextSetup"用于通过springmvc配置来测试。

 

Performing Requests

执行请求

 

Its easy to perform requests using any HTTP method:

可以使用任何HTTP方法来执行请求:

 

mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON));

 

You can also perform file upload requests that internally use MockMultipartHttpServletRequest so that there is no actual parsing of a multipart request but rather you have to set it up:

你也可以执行文件上传请求使用MockMultipartHttpServletRequest因此没有实际的解析多个请求但是你必须设置如下:

 

mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8")));

 

You can specify query parameters in URI template style:

你可以定义请求参数以URI模板风格:

 

mockMvc.perform(get("/hotels?foo={foo}", "bar"));

 

Or you can add Servlet request parameters representing either query of form parameters:

或者你可以添加Servlet请求参数代表格式化参数的请求:

 

mockMvc.perform(get("/hotels").param("foo", "bar"));

 

If application code relies on Servlet request parameters and doesnt check the query string explicitly (as is most often the case) then it doesnt matter which option you use. Keep in mind however that query params provided with the URI template will be decoded while request parameters provided through the param(…​) method are expected to already be decoded.

如果应用代码依赖Servlet请求参数并且不需要明确检查查询字符串(在大部分情况下)就不关心你使用的选项。保证通过URI模板提供的查询参数将被解码当请求参数通过param方法提供并且期望已经被解码。

 

In most cases its preferable to leave out the context path and the Servlet path from the request URI. If you must test with the full request URI, be sure to set the contextPath and servletPath accordingly so that request mappings will work:

在大部分情况下最好不要使用来自请求URI的上下文路径和servlet路径。如果你必须使用全部的请求URI测试,保证contestPathservletPath依据的请求mapping可以工作:

 

mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main"))

 

Looking at the above example, it would be cumbersome to set the contextPath and servletPath with every performed request. Instead you can set up default request properties:

查看上面的例子,比较复杂的设置了contestPathservletPath在每个请求执行时。作为替代你可以设置默认的请求属性:

 

public class MyWebTests {

 

    private MockMvc mockMvc;

 

    @Before

    public void setup() {

        mockMvc = standaloneSetup(new AccountController())

            .defaultRequest(get("/")

            .contextPath("/app").servletPath("/main")

            .accept(MediaType.APPLICATION_JSON).build();

    }

 

The above properties will affect every request performed through the MockMvc instance. If the same property is also specified on a given request, it overrides the default value. That is why the HTTP method and URI in the default request dont matter since they must be specified on every request.

上面的属性将会影响每个请求执行通过MockMvc实例。如果相同的属性被定义在给定的请求上,将覆盖默认值。这也是为什么HTTP方法和URI在一个默认的请求中却不关心他们定义在每个请求中。

 

Defining Expectations

定义预期值

 

Expectations can be defined by appending one or more .andExpect(..) calls after performing a request:

可以通过单个或多个.andExpect来定义预期值在执行每个请求后:

 

mockMvc.perform(get("/accounts/1")).andExpect(status().isOk());

 

MockMvcResultMatchers.* provides a number of expectations, some of which are further nested with more detailed expectations.

MockMvcResultMatchers.*提供了一系列预期,一些内置了更多细节的预期值。

 

Expectations fall in two general categories. The first category of assertions verifies properties of the response: for example, the response status, headers, and content. These are the most important results to assert.

预期分为两个部分。断言的第一部分沿着响应的属性:例如,响应状态、响应头和响应内容。这些对于断言来说很重要。

 

The second category of assertions goes beyond the response. These assertions allow one to inspect Spring MVC specific aspects such as which controller method processed the request, whether an exception was raised and handled, what the content of the model is, what view was selected, what flash attributes were added, and so on. They also allow one to inspect Servlet specific aspects such as request and session attributes.

断言的第二部分是在响应下。这些断言允许检验springmvc的特定方面例如哪个控制器方法处理请求,是否处理异常,模型的内容是什么,选择什么样的视图,添加什么flash属性等等。也允许处理特定的方法例如请求和会话属性。

 

The following test asserts that binding or validation failed:

下面的测试断言绑定或验证失败:

 

mockMvc.perform(post("/persons"))

    .andExpect(status().isOk())

    .andExpect(model().attributeHasErrors("person"));

 

Many times when writing tests, its useful to dump the results of the performed request. This can be done as follows, where print() is a static import from MockMvcResultHandlers:

很多时候在测试中,存储执行请求的结果是有用的。可以这样做,调用print的静态方法来自MockMvcResultHandlers

 

mockMvc.perform(post("/persons"))

    .andDo(print())

    .andExpect(status().isOk())

    .andExpect(model().attributeHasErrors("person"));

 

As long as request processing does not cause an unhandled exception, the print() method will print all the available result data to System.out. Spring Framework 4.2 introduced a log() method and two additional variants of the print() method, one that accepts an OutputStream and one that accepts a Writer. For example, invoking print(System.err) will print the result data to System.err; while invoking print(myWriter) will print the result data to a custom writer. If you would like to have the result data logged instead of printed, simply invoke the log() method which will log the result data as a single DEBUG message under the org.springframework.test.web.servlet.result logging category.

请求执行并且没有引发未处理的异常,print方法将打印所有的可能结果数据到System.out中。spring框架4.2引入了log方法和两个额外的print方法的重载,一个接受一个输出流并且另一个接受一个Writer。例如,调用print(System.err)将会打印结果数据到System.err中,当调用print(myWriter)将打印结果数据到一个自定义的writer中。如果你希望将结果记录下来而不是打印,调用log方法将记录结果数据作为一个简单的调试信息在org.springframework.test.web.servlet.result的日志模块中。

 

In some cases, you may want to get direct access to the result and verify something that cannot be verified otherwise. This can be achieved by appending .andReturn() after all other expectations:

在一些情况,你可能需要直接访问结果并验证一些不能被验证的情况。可以添加.andReturn在所有其他期望值之后。

 

MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn();

// ...

 

If all tests repeat the same expectations you can set up common expectations once when building the MockMvc instance:

如果所有的测试重复相同的期望你可以设置共同的期望值在构建MockMvc实例的时候:

 

standaloneSetup(new SimpleController())

    .alwaysExpect(status().isOk())

    .alwaysExpect(content().contentType("application/json;charset=UTF-8"))

    .build()

 

Note that common expectations are always applied and cannot be overridden without creating a separate MockMvc instance.

注意相同的期望值将被应用并且不能覆盖通过创建另一个独立的MockMvc实例之外。

 

When JSON response content contains hypermedia links created with Spring HATEOAS, the resulting links can be verified using JsonPath expressions:

JSOn响应内容包含超链接通过springHATEOAS创建,结果链接可以被验证通过JsonPath表达式:

 

mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON))

    .andExpect(jsonPath("$.links[?(@.rel == 'self')].href").value("http://localhost:8080/people"));

 

When XML response content contains hypermedia links created with Spring HATEOAS, the resulting links can be verified using XPath expressions:

xml响应内容包含超媒体链接通过springHATEOAS创建,结果链接可以被验证通过XPaht表达式:

 

Map ns = Collections.singletonMap("ns", "http://www.w3.org/2005/Atom");

mockMvc.perform(get("/handle").accept(MediaType.APPLICATION_XML))

    .andExpect(xpath("/person/ns:link[@rel='self']/@href", ns).string("http://localhost:8080/people"));

 

Filter Registrations

注册过滤

 

When setting up a MockMvc instance, you can register one or more Servlet Filter instances:

当设置一个MockMvc实例,你看一个注册一个或多个Servlet过滤实例:

 

mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build();

 

Registered filters will be invoked through via the MockFilterChain from spring-test, and the last filter will delegate to the DispatcherServlet.

注册过滤器将被调用通过MockFilterChain来自spring-test模块,并且最后一个过滤器将被委托给DispatcherServlet

 

Differences between Out-of-Container and End-to-End Integration Tests

容器外和点对点集成测试的不同

 

As mentioned earlier Spring MVC Test is built on the Servlet API mock objects from the spring-test module and does not use a running Servlet container. Therefore there are some important differences compared to full end-to-end integration tests with an actual client and server running.

就像前面的提到的springmvc测试被构建在servletapi模拟来自spring-test模块并且没有使用一个运行的servlet的容器。因此有一些重要的不同在点对点集成测试和实际的客户端和服务端运行。

 

The easiest way to think about this is starting with a blank MockHttpServletRequest. Whatever you add to it is what the request will be. Things that may catch you by surprise are that there is no context path by default, no jsessionid cookie, no forwarding, error, or async dispatches, and therefore no actual JSP rendering. Instead, "forwarded" and "redirected" URLs are saved in the MockHttpServletResponse and can be asserted with expectations.

最简单的方式来考虑使用一个空白的MockHttpServletRequest的开始启动。不管你添加什么都会被请求。这可能会使你感到惊讶,默认没有上下文路径、没有jsessionidcookie、没有转发、错误或异步转发并且没有实际JSP表现。作为代替,转发和重定向URL被保存在MockHttpServletResponse并且可以来断言期望。

 

This means if you are using JSPs you can verify the JSP page to which the request was forwarded, but there wont be any HTML rendered. In other words, the JSP will not be invoked. Note however that all other rendering technologies which dont rely on forwarding such as Thymeleaf, Freemarker, and Velocity will render HTML to the response body as expected. The same is true for rendering JSON, XML, and other formats via @ResponseBody methods.

这意味着如果你使用jSP你可以验证JSP页面对于请求被转发,但是不能被渲染称为HTML。换句话说,JSP将不会被调用。注意所有其他的渲染技术将不会依赖转发例如ThymeleafFreemarkerVelocity将渲染HTML对于期望的响应。这一点对于渲染JSONXML和其他通过@ResponseBody的方法都是一样的。

 

Alternatively you may consider the full end-to-end integration testing support from Spring Boot via @WebIntegrationTest. See the Spring Boot reference.

作为代替你可以考虑全面的点对点集成测试支持来自springboot通过@WebIntegrationTest。见spring boot的参考文档。

 

There are pros and cons for each approach. The options provided in Spring MVC Test are different stops on the scale from classic unit testing to full integration testing. To be certain, none of the options in Spring MVC Test fall under the category of classic unit testing, but they are a little closer to it. For example, you can isolate the web layer by injecting mocked services into controllers, in which case youre testing the web layer only through the DispatcherServlet but with actual Spring configuration, just like you might test the data access layer in isolation from the layers above. Or you can use the standalone setup focusing on one controller at a time and manually providing the configuration required to make it work.

对于每种方式都有正反面。在springmvc测试中提供的选项在一定范围对于集成测试的单元测试。确定的是,springmvc测试失败在单元测试是没有选项的,但是有一些接近。例如,你可以隔离web层通过注入模拟服务到控制器中,你可以测试web层仅仅通过DispatcherServlet配合实际的spring配置,就像你可以测试数据访问层在层之上。或者你可以使用独立的设置关注于控制器并手动提供配置更加需要。

 

Another important distinction when using Spring MVC Test is that conceptually such tests are on the inside of the server-side so you can check what handler was used, if an exception was handled with a HandlerExceptionResolver, what the content of the model is, what binding errors there were, etc. That means its easier to write expectations since the server is not a black box as it is when testing it through an actual HTTP client. This is generally an advantage of classic unit testing, that its easier to write, reason about, and debug but does not replace the need for full integration tests. At the same time its important not to lose sight of the fact that the response is the most important thing to check. In short, there is room here for multiple styles and strategies of testing even within the same project.

另一个重要的区别在使用springmvc测试中就是概念上这样的测试在服务端的内部因此你可以检查哪个处理器被使用,如果一个异常被处理通过HandlerExceptionResolver,模型的内容是什么、绑定了什么错误等等。这意味着可以简单的书写期望由于服务器不是一个黑盒当通过实际的HTTP客户端测试他的时候。这是普通单元测试的优点,可以简单的书写、说明原因、调试但是不会替代全面的集成测试。同时他也是重要的不需要忽略实际并且响应是最重要的检查内容。简而言之,这里对于多种方式都有一定的空间并且测试的策略在相同的项目中。

 

Further Server-Side Test Examples

更多服务端测试的例子

 

The frameworks own tests include many sample tests intended to demonstrate how to use Spring MVC Test. Browse these examples for further ideas. Also the spring-mvc-showcase has full test coverage based on Spring MVC Test.

框架自己的测试包括许多测试案例用于示范如何使用springmvc测试。浏览这些例子可以获得更多的启示。并且spring-mvc-showcase有全部的测试基于springmvc测试。

 

15.6.2 HtmlUnit Integration

HtmlUnit的集成

 

Spring provides integration between MockMvc and HtmlUnit. This simplifies performing end-to-end testing when using HTML based views. This integration enables developers to:

spring提供了集成了MockMvcHtmlUnit。这么做简化了点对点的测试当使用基于HTML的视图时。这个集成允许开发者:

 

    Easily test HTML pages using tools such as HtmlUnit, WebDriver, & Geb without the need to deploy to a Servlet container

简单测试HTML页面通过使用类似HtmlUnitWebDriverGeb这样的工具而不需要部署一个Servlet容器

    Test JavaScript within pages

在页面中测试JavaScript

    Optionally test using mock services to speed up testing

有选择的测试模拟服务来提高测试效率

    Share logic between in-container end-to-end tests and out-of-container integration tests

共享逻辑在容器点对点测试和容器外的集成测试中

 

[Note]

注意

 

MockMvc works with templating technologies that do not rely on a Servlet Container (e.g., Thymeleaf, Freemarker, Velocity, etc.), but it does not work with JSPs since they rely on the Servlet Container.

MockMvc使用模板技术不需要依赖Servlet容器(例如,ThymeleafFreemarkerVelocity等等),但是无法使用JSP因为他依赖Servlet容器。

 

Why HtmlUnit Integration?

为什么集成HtmlUnit

 

The most obvious question that comes to mind is, "Why do I need this?". The answer is best found by exploring a very basic sample application. Assume you have a Spring MVC web application that supports CRUD operations on a Message object. The application also supports paging through all messages. How would you go about testing it?

最明显的问题来自大脑,“为什么我们需要这个”?答案是在基本的案例应用中发现的。假设你有一个springmvcweb应用支持CRUD操作对于一个Messageobject。应用也支持分页通过所有的消息。那你是怎么测试他的?

 

With Spring MVC Test, we can easily test if we are able to create a Message.

springmvc测试,我们可以简单测试如果我们可以创建一个Message

 

MockHttpServletRequestBuilder createMessage = post("/messages/")

.param("summary", "Spring Rocks")

.param("text", "In case you didn't know, Spring Rocks!");

 

mockMvc.perform(createMessage)

.andExpect(status().is3xxRedirection())

.andExpect(redirectedUrl("/messages/123"));

 

What if we want to test our form view that allows us to create the message? For example, assume our form looks like the following snippet:

如果我们希望测试我们视图允许我们来创建message?例如,假设我们的形式看起来如下面的片段:

 

  

 

  

  

 

  

  

 

  

  

 

How do we ensure that our form will produce the correct request to create a new message? A naive attempt would look like this:

我们如何保证我们的形式将会引起正确的请求来创建一个新的message?一个比较天真的尝试可能如下:

 

mockMvc.perform(get("/messages/form"))

.andExpect(xpath("//input[@name='summary']").exists())

.andExpect(xpath("//textarea[@name='text']").exists());

 

This test has some obvious drawbacks. If we update our controller to use the parameter message instead of text, our form test would continue to pass even though the HTML form is out of synch with the controller. To resolve this we can combine our two tests.

这个测试有明显的缺点。如果我们更新我们的控制器来使用参数信息来代替文本,我们的测试将继续以HTML形式并且在控制器的控制之外。为了解决问题我们可以合并我们的两个测试。

 

String summaryParamName = "summary";

String textParamName = "text";

mockMvc.perform(get("/messages/form"))

.andExpect(xpath("//input[@name='" + summaryParamName + "']").exists())

.andExpect(xpath("//textarea[@name='" + textParamName + "']").exists());

 

MockHttpServletRequestBuilder createMessage = post("/messages/")

.param(summaryParamName, "Spring Rocks")

.param(textParamName, "In case you didn't know, Spring Rocks!");

 

mockMvc.perform(createMessage)

.andExpect(status().is3xxRedirection())

.andExpect(redirectedUrl("/messages/123"));

 

This would reduce the risk of our test incorrectly passing, but there are still some problems.

这会减少我们测试错误通过的风险,但是依然存在一些问题。

 

    What if we have multiple forms on our page? Admittedly we could update our xpath expressions, but they get more complicated the more factors we take into account (Are the fields the correct type? Are the fields enabled? etc.).

如果我们有多个页面的形式?无可否认我们可以更新我们的xpath表达式,但是这会更加复杂会面临更多的因素需要我们来考虑(field都是正确的类型吗?field都是允许访问的吗?等等)

    Another issue is that we are doing double the work we would expect. We must first verify the view, and then we submit the view with the same parameters we just verified. Ideally this could be done all at once.

另一个问题是我们做了我们期望的两倍工作。我们必须验证视图并且我们提交的视图通过相同的参数我们刚刚验证过。理想上他应该被只处理一次。

    Finally, there are some things that we still cannot account for. For example, what if the form has JavaScript validation that we wish to test as well?

最终,有一些事情我们依然不能避免。例如,如果是JavaScript验证的形式我们也希望测试?

 

The overall problem is that testing a web page does not involve a single interaction. Instead, it is a combination of how the user interacts with a web page and how that web page interacts with other resources. For example, the result of a form view is used as the input to a user for creating a message. In addition, our form view may potentially utilize additional resources which impact the behavior of the page, such as JavaScript validation.

全部的问题是测试一个web页面不需要调用一个单独的互动。作为代替,可以合并用户相互作用通过web页面和web页面与其他资源的相互作用。例如,视图的结果被用于作为一个输入来创建message。此外我们的视图可以利用额外的资源影响页面的行为,例如JavaScript的验证。

 

Integration testing to the rescue?

集成测试用于解救?

 

To resolve the issues above we could perform end-to-end integration testing, but this has some obvious drawbacks. Consider testing the view that allows us to page through the messages. We might need the following tests.

为了解决问题,我们可以实现点对点的集成测试,但是有一些明显的缺点。考虑测试视图允许页面通过message。我们可能需要下面的测试。

 

    Does our page display a notification to the user indicating that no results are available when the messages are empty?

我们的页面显示一个通知对于用户暗示没有结果可用当message是空的时候?

    Does our page properly display a single message?

我们的页面是否可以显示单个message

    Does our page properly support paging?

我们的页面是否支持分页?

 

To set up these tests, we would need to ensure our database contained the proper messages in it. This leads to a number of additional challenges.

为了设置这些测试,我们需要保证数据库包含适当的信息在里面。这会引起一系列额外的挑战。

 

    Ensuring the proper messages are in the database can be tedious; consider foreign key constraints.

保证适当的数据库信息可以被处理;考虑外键约束。

    Testing can become slow since each test would need to ensure that the database is in the correct state.

测试可以变慢自从每个测试需要保证数据库在正确的状态中。

    Since our database needs to be in a specific state, we cannot run tests in parallel.

自从我们的数据库需要在特定的状态中,我们不能并行的执行测试。

    Performing assertions on things like auto-generated ids, timestamps, etc. can be difficult.

执行断言就像自动生成id、时间戳等等,是很困难的。

 

These challenges do not mean that we should abandon end-to-end integration testing altogether. Instead, we can reduce the number of end-to-end integration tests by refactoring our detailed tests to use mock services which will execute much faster, more reliably, and without side effects. We can then implement a small number of true end-to-end integration tests that validate simple workflows to ensure that everything works together properly.

这些挑战不会意味着我们放弃点对点的集成测试。作为代替,我们可以减少点对点集成测试的数量通过重构我们的测试细节使用模拟服务可以执行的更加快速、更加可靠并且不会有问题。我们可以实现少量的点对点集成测试来验证简单的工作流来保证每一件事情都可以正常工作。

 

Enter HtmlUnit Integration

进入HtmlUnit集成

 

So how can we achieve a balance between testing the interactions of our pages and still retain good performance within our test suite? The answer is: "By integrating MockMvc with HtmlUnit."

因此我们是否可以获得一个平衡在测试我们的页面和保留较好的性能在我们的测试中?答案是:“通过使用MockMvcHtmlUnit”。

 

HtmlUnit Integration Options

HtmlUnit集成选项

 

There are a number of ways to integrate MockMvc with HtmlUnit.

有一些方式来集成MockMvcHtmlUnit

 

    MockMvc and HtmlUnit: Use this option if you want to use the raw HtmlUnit libraries.

MockMvcHtmlUnit:使用这个选项如果你希望使用纯的HtmlUnit

    MockMvc and WebDriver: Use this option to ease development and reuse code between integration and end-to-end testing.

MockMvcWebDriver:使用这个选项可以便于开发并简化代码在集成和点对点测试中

    MockMvc and Geb: Use this option if you would like to use Groovy for testing, ease development, and reuse code between integration and end-to-end testing.

MockMvcGeb:使用这个选项如果你希望使用Groovy在测试中,简单开发并且减少代码在集成测试和点对点测试中。

 

MockMvc and HtmlUnit

 

This section describes how to integrate MockMvc and HtmlUnit. Use this option if you want to use the raw HtmlUnit libraries.

这节描述了如何集成MockMvcHtmlUnit。使用这个选项如果你希望使用纯HtmlUnit库。

 

MockMvc and HtmlUnit Setup

 

First, make sure that you have included a test dependency on net.sourceforge.htmlunit:htmlunit. In order to use HtmlUnit with Apache HttpComponents 4.5+, you will need to use HtmlUnit 2.18 or higher.

首先,保证你包含了测试依赖net.sourceforge.htmlunit:htmlunit。为了使用HtmlUnitApache HttpComponents4.5以上的版本,你将需要使用HtmlUnit2.18及以上的版本。

 

We can easily create an HtmlUnit WebClient that integrates with MockMvc using the MockMvcWebClientBuilder as follows.

我们可以简单的创建一个HtmlUnitWebClient来集成MockMvc使用MockMvcWebClientBuilder如下。

 

@Autowired

WebApplicationContext context;

 

WebClient webClient;

 

@Before

public void setup() {

webClient = MockMvcWebClientBuilder

.webAppContextSetup(context)

.build();

}

 

[Note]

注意

 

This is a simple example of using MockMvcWebClientBuilder. For advanced usage see the section called Advanced MockMvcWebClientBuilder

这是一个简单的例子使用了MockMvcWebClientBuilder。关于高级使用见“高级的MockMvcWebClientBuilder”。

 

This will ensure that any URL referencing localhost as the server will be directed to our MockMvc instance without the need for a real HTTP connection. Any other URL will be requested using a network connection as normal. This allows us to easily test the use of CDNs.

这将保证任何URL引用本地作为一个服务器将可以导致我们的MockMvc实例不需要实际的HTTP连接。任何其他的URL将被通常要求使用一个网络连接。这允许我们使用简单的测试CDN的使用。

 

MockMvc and HtmlUnit Usage

 

Now we can use HtmlUnit as we normally would, but without the need to deploy our application to a Servlet container. For example, we can request the view to create a message with the following.

我们如何使用HtmlUnit作为我们正常的需要,但是不需要部署我们的应用到Servlet容器。例如,我们可以请求视图来创建message如下。

 

HtmlPage createMsgFormPage = webClient.getPage("http://localhost/messages/form");

 

[Note]

注意

 

The default context path is "". Alternatively, we can specify the context path as illustrated in the section called Advanced MockMvcWebClientBuilder.

默认的上下文路径是“”。作为代替,我们可以定义上下文路径描述在“高级的MockMvcWebClientBuilder”章节中。

 

Once we have a reference to the HtmlPage, we can then fill out the form and submit it to create a message.

一旦我们有了一个HtmlPage的引用,我们可以填充形式并提交来创建一个message

 

HtmlForm form = createMsgFormPage.getHtmlElementById("messageForm");

HtmlTextInput summaryInput = createMsgFormPage.getHtmlElementById("summary");

summaryInput.setValueAttribute("Spring Rocks");

HtmlTextArea textInput = createMsgFormPage.getHtmlElementById("text");

textInput.setText("In case you didn't know, Spring Rocks!");

HtmlSubmitInput submit = form.getOneHtmlElementByAttribute("input", "type", "submit");

HtmlPage newMessagePage = submit.click();

 

Finally, we can verify that a new message was created successfully. The following assertions use the AssertJ library.

最终,我们可以验证一个新的message是否创建成功。下面的断言使用了AssertJ库。

 

assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123");

String id = newMessagePage.getHtmlElementById("id").getTextContent();

assertThat(id).isEqualTo("123");

String summary = newMessagePage.getHtmlElementById("summary").getTextContent();

assertThat(summary).isEqualTo("Spring Rocks");

String text = newMessagePage.getHtmlElementById("text").getTextContent();

assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!");

 

This improves on our MockMvc test in a number of ways. First we no longer have to explicitly verify our form and then create a request that looks like the form. Instead, we request the form, fill it out, and submit it, thereby significantly reducing the overhead.

这提高了我们的MockMvc测试在一定的方面。首先我们不需要明确验证我们的形式并且创建一个请求就像形式一样。作为代替,我们请求形式、填充并提交,从而减少我们的总花费。

 

Another important factor is that HtmlUnit uses the Mozilla Rhino engine to evaluate JavaScript. This means that we can test the behavior of JavaScript within our pages as well!

另一个重要的因素是HtmlUnit使用了Mozilla Rhino引擎来处理JavaScript。这意味着我们可以测试JavaScript的行为而不需要我们的页面!

 

Refer to the HtmlUnit documentation for additional information about using HtmlUnit.

参考HtmlUnit的文档来了解额外的信息。

 

Advanced MockMvcWebClientBuilder

高级的MockMvcWebClientBuilder

 

In the examples so far, we have used MockMvcWebClientBuilder in the simplest way possible, by building a WebClient based on the WebApplicationContext loaded for us by the Spring TestContext Framework. This approach is repeated here.

在之前的例子中,我们已经使用了MockMvcWebClientBuilder通过简单的形式,通过构建一个WebClient基于WebApplicationContext使用SpringTestContext框架来加载。这个方式再展示一次。

 

@Autowired

WebApplicationContext context;

 

WebClient webClient;

 

@Before

public void setup() {

webClient = MockMvcWebClientBuilder

.webAppContextSetup(context)

.build();

}

 

We can also specify additional configuration options.

我们也可以定义额外的配置选项。

 

WebClient webClient;

 

@Before

public void setup() {

webClient = MockMvcWebClientBuilder

// demonstrates applying a MockMvcConfigurer (Spring Security)

.webAppContextSetup(context, springSecurity())

// for illustration only - defaults to ""

.contextPath("")

// By default MockMvc is used for localhost only;

// the following will use MockMvc for example.com and example.org as well

.useMockMvcForHosts("example.com","example.org")

.build();

}

 

As an alternative, we can perform the exact same setup by configuring the MockMvc instance separately and supplying it to the MockMvcWebClientBuilder as follows.

作为代替,我们可以执行相同的设置通过分别配置MockMvc实例并将其加入MockMvcWebClientBuilder如下。

 

MockMvc mockMvc = MockMvcBuilders

.webAppContextSetup(context)

.apply(springSecurity())

.build();

 

webClient = MockMvcWebClientBuilder

.mockMvcSetup(mockMvc)

// for illustration only - defaults to ""

.contextPath("")

// By default MockMvc is used for localhost only;

// the following will use MockMvc for example.com and example.org as well

.useMockMvcForHosts("example.com","example.org")

.build();

 

This is more verbose, but by building the WebClient with a MockMvc instance we have the full power of MockMvc at our fingertips.

这比较冗长,但是通过WebClient来构建使用MockMvc实例我们已经有了强力的工具MockMvc

 

[Tip]

提示

 

For additional information on creating a MockMvc instance refer to the section called Setup Options.

关于额外的信息创建一个MockMvc实例参考章节“设置选项”。

 

MockMvc and WebDriver

 

In the previous sections, we have seen how to use MockMvc in conjunction with the raw HtmlUnit APIs. In this section, we will leverage additional abstractions within the Selenium WebDriver to make things even easier.

在前面的章节中,我们了解了如何使用MockMvc配合HtmlUnitAPI。在这一节中,我们将利用额外的抽象通过Selenium WebDriver使得事情更加简单。

 

Why WebDriver and MockMvc?

 

We can already use HtmlUnit and MockMvc, so why would we want to use WebDriver? The Selenium WebDriver provides a very elegant API that allows us to easily organize our code. To better understand, lets explore an example.

我们已经使用了HtmlUnitMockMvc,因此为什么我们希望使用WebDriverSelenium WebDriver提供了一个非常好的API允许我们简单的组织我们的代码。为了便于理解,让我们来看一个例子。

 

[Note]

注意

 

Despite being a part of Selenium, WebDriver does not require a Selenium Server to run your tests.

尽管作为Selenium的一部分,WebDriver不要求使用Selenium来运行你的测试。

 

Suppose we need to ensure that a message is created properly. The tests involve finding the HTML form input elements, filling them out, and making various assertions.

假设我们需要保证message被正确创建。测试调用HTML形式的输入元素、填充他们并且使用不同断言。

 

This approach results in numerous, separate tests because we want to test error conditions as well. For example, we want to ensure that we get an error if we fill out only part of the form. If we fill out the entire form, the newly created message should be displayed afterwards.

这个结果是多样的,分开测试因为我们希望测试错误的条件。例如,我们希望保证我们获得一个错误如果我们只填充了部分的表格。如果我们填充了整个表格则创建message显示在后面。

 

If one of the fields were named "summary", then we might have something like the following repeated in multiple places within our tests.

如果其中一个field名字为summary,我们可以有下面的代码在我们测试的多个位置重复出现。

 

HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");

summaryInput.setValueAttribute(summary);

 

So what happens if we change the id to "smmry"? Doing so would force us to update all of our tests to incorporate this change! Of course, this violates the DRY Principle; so we should ideally extract this code into its own method as follows.

因此如果我们将id改为smmry会发生什么?这么做将强制我们修改测试中每个位置来改变错误!当然,这破坏了DRY原则,因此我们应当提出这些代码如下。

 

public HtmlPage createMessage(HtmlPage currentPage, String summary, String text) {

setSummary(currentPage, summary);

// ...

}

 

public void setSummary(HtmlPage currentPage, String summary) {

HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");

summaryInput.setValueAttribute(summary);

}

 

This ensures that we do not have to update all of our tests if we change the UI.

这保证我们不需要修改我们改变UI的每个位置。

 

We might even take this a step further and place this logic within an Object that represents the HtmlPage we are currently on.

我们甚至可以更进一步并且替换逻辑在object中代表我们当前的HtmlPage

 

public class CreateMessagePage {

 

final HtmlPage currentPage;

 

final HtmlTextInput summaryInput;

 

final HtmlSubmitInput submit;

 

public CreateMessagePage(HtmlPage currentPage) {

this.currentPage = currentPage;

this.summaryInput = currentPage.getHtmlElementById("summary");

this.submit = currentPage.getHtmlElementById("submit");

}

 

public T createMessage(String summary, String text) throws Exception {

setSummary(summary);

 

HtmlPage result = submit.click();

boolean error = CreateMessagePage.at(result);

 

return (T) (error ? new CreateMessagePage(result) : new ViewMessagePage(result));

}

 

public void setSummary(String summary) throws Exception {

summaryInput.setValueAttribute(summary);

}

 

public static boolean at(HtmlPage page) {

return "Create Message".equals(page.getTitleText());

}

}

 

Formerly, this pattern is known as the Page Object Pattern. While we can certainly do this with HtmlUnit, WebDriver provides some tools that we will explore in the following sections to make this pattern much easier to implement.

以前,这个模式被称为Page Object Pattern。当我们可以使用HtmlUnit时,WebDriver提供了相同的工具使得我们可以探索后面的章节使得这个模式更加易于实现。

 

MockMvc and WebDriver Setup

 

To use Selenium WebDriver with the Spring MVC Test framework, make sure that your project includes a test dependency on org.seleniumhq.selenium:selenium-htmlunit-driver.

为了使用Selenium WebDriver配合springmvc的测试框架,保证你的项目包括一个测试依赖org.seleniumhq.selenium:selenium-htmlunit-driver

 

We can easily create a Selenium WebDriver that integrates with MockMvc using the MockMvcHtmlUnitDriverBuilder as follows.

我们可以简单的创建Selenium WebDriver集成MockMvc使用MockMvcHtmlUnitDriverBuilder如下。

 

@Autowired

WebApplicationContext context;

 

WebDriver driver;

 

@Before

public void setup() {

driver = MockMvcHtmlUnitDriverBuilder

.webAppContextSetup(context)

.build();

}

 

[Note]

注意

 

This is a simple example of using MockMvcHtmlUnitDriverBuilder. For more advanced usage, refer to the section called Advanced MockMvcHtmlUnitDriverBuilder

这是一个使用MockMvcHtmlUnitDriverBuilder的简单案例。关于更加高级的用法,参考章节“高级MockMvcHtmlUnitDriverBuilder

 

This will ensure that any URL referencing localhost as the server will be directed to our MockMvc instance without the need for a real HTTP connection. Any other URL will be requested using a network connection as normal. This allows us to easily test the use of CDNs.

这会保证人恶化URL引用localhost作为服务器将指向MockMvc实例而不需要真实的HTTP连接。任何其他的URL将使用普通的请求。这允许我们简单的测试CDN的使用。

 

MockMvc and WebDriver Usage

 

Now we can use WebDriver as we normally would, but without the need to deploy our application to a Servlet container. For example, we can request the view to create a message with the following.

现在我们可以使用WebDriver,但是不需要部署我们的应用到Servlet容器中。例如,我们可以请求视图来创建message如下。

 

CreateMessagePage page = CreateMessagePage.to(driver);

 

We can then fill out the form and submit it to create a message.

我们可以填写表单并提交来创建message

 

ViewMessagePage viewMessagePage =

page.createMessage(ViewMessagePage.class, expectedSummary, expectedText);

 

This improves on the design of our HtmlUnit test by leveraging the Page Object Pattern. As we mentioned in the section called Why WebDriver and MockMvc?, we can use the Page Object Pattern with HtmlUnit, but it is much easier with WebDriver. Lets take a look at our new CreateMessagePage implementation.

这改进了我们HtmlUnit测试的设计通过扩展Page Object Pattern。就像我们提到的章节“为什么使用WebDriverMockMvc?”,我们可以使用Page Object Pattern配合HtmlUnit,但是他在使用WebDriver时很简单。让我们来看一下我们新的CreateMessagePage实现。

 

public class CreateMessagePage

extends AbstractPage { 1

 

2

private WebElement summary;

private WebElement text;

 

3

@FindBy(css = "input[type=submit]")

private WebElement submit;

 

public CreateMessagePage(WebDriver driver) {

super(driver);

}

 

public T createMessage(Class resultPage, String summary, String details) {

this.summary.sendKeys(summary);

this.text.sendKeys(details);

this.submit.click();

return PageFactory.initElements(driver, resultPage);

}

 

public static CreateMessagePage to(WebDriver driver) {

driver.get("http://localhost:9990/mail/messages/form");

return PageFactory.initElements(driver, CreateMessagePage.class);

}

}

 

1 The first thing you will notice is that CreateMessagePage extends the AbstractPage. We wont go over the details of AbstractPage, but in summary it contains common functionality for all of our pages. For example, if our application has a navigational bar, global error messages, etc., this logic can be placed in a shared location.

首先你需要注意的是CreateMessagePage继承了AbstractPage。我们不会讨论AbstractPage的细节,但是总结一下他包含了通用的方法对于所有页面。例如,如果我们的应用有一个导航栏、全局的错误信息等等,这些逻辑可以放置在共享的位置。

 

2 The next thing you will notice is that we have a member variable for each of the parts of the HTML page that we are interested in. These are of type WebElement. WebDriver's PageFactory allows us to remove a lot of code from the HtmlUnit version of CreateMessagePage by automatically resolving each WebElement. The PageFactory#initElements(WebDriver,Class) method will automatically resolve each WebElement by using the field name and looking it up by the id or name of the element within the HTML page.

下一件你将会注意到我们有一个成员变量对于每个我们感兴趣的HTML页面的部分。他们是WebElement类型的。WebDriver工厂允许我们去掉一些HtmlUnit中的代码通过自动处理每个WebElementPageFactory#initElements(WebDriver,Class)方法将自动处理每个WebElement通过使用field的名字和通过id查找每个元素在HTML页面。

 

3 We can use the @FindBy annotation to override the default lookup behavior. Our example demonstrates how to use the @FindBy annotation to look up our submit button using a css selector, input[type=submit].

我们可以使用@FindBy注解来覆盖默认的查找行为。我们的例子描述了如何使用@FindBy注解来查找我们的提交按钮通过使用css选择器,input[type=submit]

 

Finally, we can verify that a new message was created successfully. The following assertions use the FEST assertion library.

最后,我们可以验证新的message是否创建成功。下面的断言使用了FEST断言库。

 

assertThat(viewMessagePage.getMessage()).isEqualTo(expectedMessage);

assertThat(viewMessagePage.getSuccess()).isEqualTo("Successfully created a new message");

 

We can see that our ViewMessagePage allows us to interact with our custom domain model. For example, it exposes a method that returns a Message object.

我们可以看到ViewMessagePage允许我们来集成自定义的域模型。例如,他暴露了一个方法来返回Messageobject

 

public Message getMessage() throws ParseException {

Message message = new Message();

message.setId(getId());

message.setCreated(getCreated());

message.setSummary(getSummary());

message.setText(getText());

return message;

}

 

We can then leverage the rich domain objects in our assertions.

我们可以利用富领域object在我们的断言中。

 

Lastly, dont forget to close the WebDriver instance when the test is complete.

最后,不要忘记当测试结束的时候关闭WebDriver实例

 

@After

public void destroy() {

if (driver != null) {

driver.close();

}

}

 

For additional information on using WebDriver, refer to the Selenium WebDriver documentation.

更多使用WebDriver的信息,参考Selenium WebDriver的文档。

 

Advanced MockMvcHtmlUnitDriverBuilder

高级的MockMvcHtmlUnitDriverBuilder

 

In the examples so far, we have used MockMvcHtmlUnitDriverBuilder in the simplest way possible, by building a WebDriver based on the WebApplicationContext loaded for us by the Spring TestContext Framework. This approach is repeated here.

到目前为止的例子中,我们已经使用了MockMvcHtmlUnitDriverBuilder以一些简单的方式,通过构建WebDriver基于WebApplicationContext使用springTestContext框架加载。这样的例子再展示一次。

 

@Autowired

WebApplicationContext context;

 

WebDriver driver;

 

@Before

public void setup() {

driver = MockMvcHtmlUnitDriverBuilder

.webAppContextSetup(context)

.build();

}

 

We can also specify additional configuration options.

我们也可以定义额外的配置选项。

 

WebDriver driver;

 

@Before

public void setup() {

driver = MockMvcHtmlUnitDriverBuilder

// demonstrates applying a MockMvcConfigurer (Spring Security)

.webAppContextSetup(context, springSecurity())

// for illustration only - defaults to ""

.contextPath("")

// By default MockMvc is used for localhost only;

// the following will use MockMvc for example.com and example.org as well

.useMockMvcForHosts("example.com","example.org")

.build();

}

 

As an alternative, we can perform the exact same setup by configuring the MockMvc instance separately and supplying it to the MockMvcHtmlUnitDriverBuilder as follows.

作为替代,我们可以通过MockMvc实例的分离来配置相同的设置并且加入到MockMvcHtmlUnitDriverBuilder中如下。

 

MockMvc mockMvc = MockMvcBuilders

.webAppContextSetup(context)

.apply(springSecurity())

.build();

 

driver = MockMvcHtmlUnitDriverBuilder

.mockMvcSetup(mockMvc)

// for illustration only - defaults to ""

.contextPath("")

// By default MockMvc is used for localhost only;

// the following will use MockMvc for example.com and example.org as well

.useMockMvcForHosts("example.com","example.org")

.build();

 

This is more verbose, but by building the WebDriver with a MockMvc instance we have the full power of MockMvc at our fingertips.

这是比较冗长的,但是通过使用MockMvc实例来构建WebDriver我们已经完全掌握了。

 

[Tip]

提示

 

For additional information on creating a MockMvc instance refer to the section called Setup Options.

关于额外的信息来创建一个MockMvc实例参考“设置选项”

 

MockMvc and Geb

 

In the previous section, we saw how to use MockMvc with WebDriver. In this section, we will use Geb to make our tests even Groovy-er.

在前面的章节,我们看到了使用WebDriver来配合MockMvc。在这一节,我们将使用Geb使得我们的测试更加Groovy-er

 

Why Geb and MockMvc?

 

Geb is backed by WebDriver, so it offers many of the same benefits that we get from WebDriver. However, Geb makes things even easier by taking care of some of the boilerplate code for us.

Geb依靠WebDriver,因此他提供了许多我们从WebDriver中得到的好处。然而,Geb使得一切更加简单通过替我们处理一些引用文件。

 

MockMvc and Geb Setup

 

We can easily initialize a Geb Browser with a Selenium WebDriver that uses MockMvc as follows.

我们可以简单初始化一个Geb浏览器使用一个Selenium WebDriver如下来使用MockMvc

 

def setup() {

browser.driver = MockMvcHtmlUnitDriverBuilder

.webAppContextSetup(context)

.build()

}

 

[Note]

注意

 

This is a simple example of using MockMvcHtmlUnitDriverBuilder. For more advanced usage, refer to the section called Advanced MockMvcHtmlUnitDriverBuilder

这是使用MockMvcHtmlUnitDriverBuilder的简单示例。更多高级的用法,参考“高级的MockMvcHtmlUnitDriverBuilder”。

 

This will ensure that any URL referencing localhost as the server will be directed to our MockMvc instance without the need for a real HTTP connection. Any other URL will be requested using a network connection as normal. This allows us to easily test the use of CDNs.

这将保证URL引用localhost作为服务器将指向我们的MockMvc实例而不需要实际的HTTP连接。而其他的URL将请求正常的网络连接。这使得我们可以更好的使用CDN来测试。

 

MockMvc and Geb Usage

 

Now we can use Geb as we normally would, but without the need to deploy our application to a Servlet container. For example, we can request the view to create a message with the following:

现在我们可以使用Geb作为我们正常的需要,但是不需要将我们的应用部署到Servlet容器中。例如,我们可以请求视图来创建message如下:

 

to CreateMessagePage

 

We can then fill out the form and submit it to create a message.

我们可以填写表格并提交来创建message

 

when:

form.summary = expectedSummary

form.text = expectedMessage

submit.click(ViewMessagePage)

 

Any unrecognized method calls or property accesses/references that are not found will be forwarded to the current page object. This removes a lot of the boilerplate code we needed when using WebDriver directly.

任何未识别的方法调用或属性访问或引用不会被使用将被转发到当前的页面object。这去除了许多我们直接使用WebDriver时的引用代码。

 

As with direct WebDriver usage, this improves on the design of our HtmlUnit test by leveraging the Page Object Pattern. As mentioned previously, we can use the Page Object Pattern with HtmlUnit and WebDriver, but it is even easier with Geb. Lets take a look at our new Groovy-based CreateMessagePage implementation.

作为直接WebDriver使用,这改进了我们HtmlUnit测试的设计扩展了Page Object Pattern。就像之前提到的,我们可以使用Page Object Pattern通过HtmlUnitWebDriver,但是使用Geb会更好。让我们看一下新的基于GroovyCreateMessagePage实现。

 

class CreateMessagePage extends Page {

static url = 'messages/form'

static at = { assert title == 'Messages : Create'; true }

static content =  {

submit { $('input[type=submit]') }

form { $('form') }

errors(required:false) { $('label.error, .alert-error')?.text() }

}

}

 

The first thing you will notice is that our CreateMessagePage extends Page. We wont go over the details of Page, but in summary it contains common functionality for all of our pages. The next thing you will notice is that we define a URL in which this page can be found. This allows us to navigate to the page as follows.

首先你将会注意到我们的CreateMessagePage继承了Page。我们不需要了解Page的细节,但是总的来说他包含了一些通用的功能用于我们的页面。下一件你将会注意到我们定义了一个URL在这个页面可以被发现。这允许我们如下来导航页面。

 

to CreateMessagePage

 

We also have an at closure that determines if we are at the specified page. It should return true if we are on the correct page. This is why we can assert that we are on the correct page as follows.

我们也可以在特定的页面进行关闭。他应当会返回true如果我们在当前的页面中。这也是为什么我们可以断言我们在正确的页面如下。

 

then:

at CreateMessagePage

errors.contains('This field is required.')

 

[Note]

注意

 

We use an assertion in the closure, so that we can determine where things went wrong if we were at the wrong page.

我们可以在关闭时使用断言,因此我们决定如果在错误的页面是否会出现错误。

 

Next we create a content closure that specifies all the areas of interest within the page. We can use a jQuery-ish Navigator API to select the content we are interested in.

下面我们创建一个关闭内容指定所有我们在页面中感兴趣的部分。我们可以使用jQuery-ish的导航API来选择我们感兴趣的内容。

 

Finally, we can verify that a new message was created successfully.

最后我们可以验证新创建的message的正确性。

 

then:

at ViewMessagePage

success == 'Successfully created a new message'

id

date

summary == expectedSummary

message == expectedMessage

 

For further details on how to get the most out of Geb, consult The Book of Geb users manual.

关于如何获得Geb的更多的信息,参考Geb的用户手册。

 

15.6.3 Client-Side REST Tests

客户端的REST测试

 

Client-side tests can be used to test code that internally uses the RestTemplate. The idea is to declare expected requests and to provide "stub" responses so that you can focus on testing the code in isolation, i.e. without running a server. Here is an example:

客户端的测试可以被用于测试代码集成使用RestTemplate。声明期望请求和提供stbu的响应以便你可以关注于独立的测试代码,也就是说,不需要在服务器上运行。这里有一个例子:

 

RestTemplate restTemplate = new RestTemplate();

 

MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();

mockServer.expect(requestTo("/greeting")).andRespond(withSuccess());

 

// Test code that uses the above RestTemplate ...

 

mockServer.verify();

 

In the above example, MockRestServiceServer, the central class for client-side REST tests, configures the RestTemplate with a custom ClientHttpRequestFactory that asserts actual requests against expectations and returns "stub" responses. In this case we expect a request to "/greeting" and want to return a 200 response with "text/plain" content. We could define as additional expected requests and stub responses as needed. When expected requests and stub responses are defined, the RestTemplate can be used in client-side code as usual. At the end of testing mockServer.verify() can be used to verify that all expectations have been satisfied.

在上面的例子中,MockRestServiceServer,是客户端REST测试的中心类,配置RestTemplate使用自定义的ClientHttpRequestFactory来断言实际的请求是否符合期望并返回stub影响。在这个例子中,我们期望一个请求"/greeting"可以返回200响应和"text/plain"类型的内容。我们可以定义额外的期望请求和stub响应根据需要。当期望请求和stub响应被定义,RestTemplate可以用于客户端代码。在测试mockServer.verify()的最后用于验证所有的期望都是符合的。

 

By default requests are expected in the order in which expectations were declared. You can set the ignoreExpectOrder option when building the server in which case all expectations are checked (in order) to find a match for a given request. That means requests are allowed to come in any order. Here is an example:

默认请求期望当期望被定义的时候。你可以设置ignoreExpectOrder选项来构建服务器当所有的期望都已经被检查过(按顺序)来找到一个匹配根据给定的请求。这意味着请求被允许以一定的顺序。这有一个例子:

 

server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();

 

Even with unordered requests by default each request is allowed to execute once only. The expect method provides an overloaded variant that accepts an ExpectedCount argument that specifies a count range, e.g. once, manyTimes, max, min, between, and so on. Here is an example:

对于默认无序的请求中每个请求允许执行一次。期望方法提供了一个超载的变量来接受一个ExpectedCount参数定义一个计数范围,也就是说,oncemanyTimesmaxminbetween等等。这里有一些例子:

 

RestTemplate restTemplate = new RestTemplate();

 

MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();

mockServer.expect(times(2), requestTo("/foo")).andRespond(withSuccess());

mockServer.expect(times(3), requestTo("/bar")).andRespond(withSuccess());

 

// ...

 

mockServer.verify();

 

Note that when ignoreExpectOrder is not set (the default), and therefore requests are expected in order of declaration, then that order only applies to the first of any expected request. For example if "/foo" is expected 2 times followed by "/bar" 3 times, then there should be a request to "/foo" before there is a request to "/bar" but aside from that subsequent "/foo" and "/bar" requests can come at any time.

注意当没有设置ignoreExpectOrder时,请求被期望在声明中,要求只会应用于任何需求中第一个。例如,如果"/foo"被请求2次,"/bar"被请求3次,那么foo的请求应当在bar请求之前并且后续的请求可以来自任何时候。

 

As an alternative to all of the above the client-side test support also provides a ClientHttpRequestFactory implementation that can be configured into a RestTemplate to bind it to a MockMvc instance. That allows processing requests using actual server-side logic but without running a server. Here is an example:

作为代替所有上面的客户端测试支持提供了一个ClientHttpRequestFactory实现可以被配置到RestTemplate来绑定到MockMvc实例中。这允许执行请求使用实际的服务端逻辑但是不需要在服务器上运行。这里有一个例子:

 

MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();

this.restTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(mockMvc));

 

// Test code that uses the above RestTemplate ...

 

mockServer.verify();

 

Static Imports

静态导入

 

Just like with server-side tests, the fluent API for client-side tests requires a few static imports. Those are easy to find by searching "MockRest*". Eclipse users should add "MockRestRequestMatchers.*" and "MockRestResponseCreators.*" as "favorite static members" in the Eclipse preferences under Java Editor Content Assist Favorites. That allows using content assist after typing the first character of the static method name. Other IDEs (e.g. IntelliJ) may not require any additional configuration. Just check the support for code completion on static members.

就像服务器端测试,丰富的API对于客户端测试要求一些静态的导入。他们可以轻易的被发现通过查找“MockRest*”。Eclipse用户应当添加"MockRestRequestMatchers.*""MockRestResponseCreators.*"作为"favorite static members"Eclipse的偏好在Java Editor Content Assist Favorites下。这允许使用内容辅助在键入第一个静态方法的字母。其他的IDE(例如IntelliJ)可能不需要额外的配置。只要检查代码完整对于静态成员的支持就可以。

 

Further Examples of Client-side REST Tests

客户端REST测试的更多例子

 

Spring MVC Tests own tests include example tests of client-side REST tests.

springmvc测试的自身测试包括客户端REST测试的测试案例。

 

15.7 PetClinic Example

PetClinic例子

 

The PetClinic application, available on GitHub, illustrates several features of the Spring TestContext Framework in a JUnit 4 environment. Most test functionality is included in the AbstractClinicTests, for which a partial listing is shown below:

PetClinic应用,在GitHub上可以获取,阐述了一些springTestContext框架的一些特性在JUnit4的环境中。大部分测试功能被包含在AbstractClinicTests中,其中局部列出如下:

 

import static org.junit.Assert.assertEquals;

// import ...

 

@ContextConfiguration

public abstract class AbstractClinicTests extends AbstractTransactionalJUnit4SpringContextTests {

 

    @Autowired

    protected Clinic clinic;

 

    @Test

    public void getVets() {

        Collection vets = this.clinic.getVets();

        assertEquals("JDBC query must show the same number of vets",

            super.countRowsInTable("VETS"), vets.size());

        Vet v1 = EntityUtils.getById(vets, Vet.class, 2);

        assertEquals("Leary", v1.getLastName());

        assertEquals(1, v1.getNrOfSpecialties());

        assertEquals("radiology", (v1.getSpecialties().get(0)).getName());

        // ...

    }

 

    // ...

}

 

Notes:

注意:

 

    This test case extends the AbstractTransactionalJUnit4SpringContextTests class, from which it inherits configuration for Dependency Injection (through the DependencyInjectionTestExecutionListener) and transactional behavior (through the TransactionalTestExecutionListener).

这个测试案例继承了AbstractTransactionalJUnit4SpringContextTests类,继承了配置用于依赖注入(通过DependencyInjectionTestExecutionListener)和传统行为(通过TransactionalTestExecutionListener

    The clinic instance variable — the application object being tested — is set by Dependency Injection through @Autowired semantics.

这个clinic实例变量————应用object被测试————通过@Autowired语义被依赖注入

    The getVets() method illustrates how you can use the inherited countRowsInTable() method to easily verify the number of rows in a given table, thus verifying correct behavior of the application code being tested. This allows for stronger tests and lessens dependency on the exact test data. For example, you can add additional rows in the database without breaking tests.

getVets方法声明了你可以如何使用继承的countRowsInTable来简单验证行数根据给定的表,验证应用代码正确的行为被测试。这允许更有力的测试和额外测试数据的减少。例如,你可以添加额外的行在数据库中不需要破坏测试。

    Like many integration tests that use a database, most of the tests in AbstractClinicTests depend on a minimum amount of data already in the database before the test cases run. Alternatively, you might choose to populate the database within the test fixture set up of your test cases — again, within the same transaction as the tests.

就像许多集成测试使用数据库,大部分测试在AbstractClinicTests中依赖较少的已经在数据库中的数据量在测试运行之前。此外,你可以选择填充数据库在测试工具中设置你的测试案例————再一次相同的交易作为测试。

 

The PetClinic application supports three data access technologies: JDBC, Hibernate, and JPA. By declaring @ContextConfiguration without any specific resource locations, the AbstractClinicTests class will have its application context loaded from the default location, AbstractClinicTests-context.xml, which declares a common DataSource. Subclasses specify additional context locations that must declare a PlatformTransactionManager and a concrete implementation of Clinic.

PetClinic应用支持三种数据访问技术:JDBCHibernateJPA。通过定义@ContextConfiguration不需要指定任何特定的资源位置,AbstractClinicTests将有他的应用上下文被加载根据默认的位置,AbstractClinicTests-context.xml,定义了一个通用的数据源。子类定义了额外的上下文位置必须定义PlatformTransactionManager和一个Clinic的具体实现。

 

For example, the Hibernate implementation of the PetClinic tests contains the following implementation. For this example, HibernateClinicTests does not contain a single line of code: we only need to declare @ContextConfiguration, and the tests are inherited from AbstractClinicTests. Because @ContextConfiguration is declared without any specific resource locations, the Spring TestContext Framework loads an application context from all the beans defined in AbstractClinicTests-context.xml (i.e., the inherited locations) and HibernateClinicTests-context.xml, with HibernateClinicTests-context.xml possibly overriding beans defined in AbstractClinicTests-context.xml.

例如,PetClinic测试的Hibernate实现包含下面的实现。对于这个例子,HibernateClinicTests不需要包含单行代码:我们只需要定义@ContextConfiguration并且测试继承自AbstractClinicTests。因为@ContextConfiguration被定义没有指定任何资源位置,spirngTestContext框架加载应用上下文来自所有的bean定义在AbstractClinicTests-context.xml中(也就是继承的位置)和HibernateClinicTests-context.xmlHibernateClinicTests-context.xml覆盖定义在AbstractClinicTests-context.xml中的bean

 

@ContextConfiguration

public class HibernateClinicTests extends AbstractClinicTests { }

 

In a large-scale application, the Spring configuration is often split across multiple files. Consequently, configuration locations are typically specified in a common base class for all application-specific integration tests. Such a base class may also add useful instance variables — populated by Dependency Injection, naturally — such as a SessionFactory in the case of an application using Hibernate.

在一个大型项目中,spring配置经常被分割到多个文件。因此,配置位置通常定义在一个通用的基本类对于特定应用的集成测试。这样的基类也可以添加有用的实例变量————使用依赖注入————例如使用Hibernate中的SessionFactory

 

As far as possible, you should have exactly the same Spring configuration files in your integration tests as in the deployed environment. One likely point of difference concerns database connection pooling and transaction infrastructure. If you are deploying to a full-blown application server, you will probably use its connection pool (available through JNDI) and JTA implementation. Thus in production you will use a JndiObjectFactoryBean or for the DataSource and JtaTransactionManager. JNDI and JTA will not be available in out-of-container integration tests, so you should use a combination like the Commons DBCP BasicDataSource and DataSourceTransactionManager or HibernateTransactionManager for them. You can factor out this variant behavior into a single XML file, having the choice between application server and a 'local' configuration separated from all other configuration, which will not vary between the test and production environments. In addition, it is advisable to use properties files for connection settings. See the PetClinic application for an example.

尽可能,你应当有明确的spring配置文件在你的集成而是作为在开发环境中。有人会关注数据库连接和事务机制。如果你部署应用到成熟的应用服务器,你将使用他的连接工具(可能通过JNDI)和JTA实现。在生产中你将使用JndiObjectFactoryBean用于数据源和JtaTransactionManagerJNDIJTA将不会再集成测试的容器外可用,因此你应当使用一个结合工具类似于Commons DBCP BasicDataSourceDataSourceTransactionManagerHibernateTransactionManager对于他们。你可以提出变量行为到一个单独的xml文件,有选择的在应用服务器和本地配置之间,并和其他的配置分离,将使得测试和生产环境尽量相似。此外,可以使用属性文件对于连接设置。见PetClinic应用的例子。

 

16. Further Resources

更多资源

 

Consult the following resources for more information about testing:

查阅下面的资源可以获得测试方面更多的内容

 

    JUnit: "A programmer-oriented testing framework for Java". Used by the Spring Framework in its test suite.

JUnit:“一个面向程序员的java测试框架”。可以在spring的框架中使用。

    TestNG: A testing framework inspired by JUnit with added support for annotations, test groups, data-driven testing, distributed testing, etc.

TestNG:一个测试框架由JUnit启发添加了对注解、测试组、基于数据测试、分布式测试等等。

    AssertJ: "Fluent assertions for Java" including support for Java 8 lambdas, streams, etc.

AssertJ:丰富的断言用于Java包括支持Java8lambdas、流等等。

    Mock Objects: Article in Wikipedia.

Mock Objects:维基上有介绍

    MockObjects.com: Web site dedicated to mock objects, a technique for improving the design of code within test-driven development.

MockObjects.commock objects的主题网站,用于提高设计代码的技术在基于测试的环境中。

    Mockito: Java mock library based on the test spy pattern.

Mockitojava模拟库基于测试侦查模式

    EasyMock: Java library "that provides Mock Objects for interfaces (and objects through the class extension) by generating them on the fly using Javas proxy mechanism." Used by the Spring Framework in its test suite.

EasyMockjava库“提供了模拟object用于接口(object通过类的扩展)通过生成使用java的代理机制”。可以在spring框架中使用。

    JMock: Library that supports test-driven development of Java code with mock objects.

JMock:支持测试库使用java代码和模拟object

    DbUnit: JUnit extension (also usable with Ant and Maven) targeted for database-driven projects that, among other things, puts your database into a known state between test runs.

DbUnitJUnit扩展(也可以使用AntMaven)目标是基于数据库的程序,包括其他的内容,使你的数据库在测试中保持一个已知的状态。

    The Grinder: Java load testing framework.

The Grinderjava加载的测试框架。

 

 

你可能感兴趣的:(Spring框架参考文档翻译,V4.3.4.RELEASE)