开发Java教程Spring后端开发Spring测试框架完全指南:单元测试与集成测试Diebug2026年08月25日 # Spring测试框架完全指南:单元测试与集成测试 > 本文基于DDKK.COM的Spring教程系列进行二次创作,原文链接:https://ddkk.com/category/j2ee/spring/1/index.html ## 9. Spring框架下的测试 测试乃企业级软件开发的重要组成部分之一。本章专注于讲解采用 IoC 原则进行编码而给单元测试带来的好处,以及 Spring 框架对集成测试的支持如何为测试带来帮助。(对企业开发中如何进行代码测试的详尽讨论不在本文档讨论范围之内) ## 10. 单元测试 比起传统的Java EE开发方式,依赖注入可以弱化你的代码对容器的依赖。在基于Junit或TestNG的测试代码中,无需依赖于Spring或其他容器,你只需通过new操作符,便可以创建出组成你的应用程序的各种POJO对象。而通过mock对象(以及其它各种测试技术的综合运用),你可以将被测试的代码单独隔离开来进行测试。如果你在进行架构设计时遵循了Spring所推荐的模式,那么由此带来的诸如清晰的分层、组件化等等优点也会使你更容易地对你的代码进行单元测试。例如,通过mock DAO层或Repositry接口的实现,针对服务层对象的单元测试代码并不需要真正地访问持久层的数据。 真正的单元测试代码运行的非常快,因为并不需要一整套的运行时架构去支持测试的运转。所以在开发中强调真正的单元测试必须成为编码方法论中的一部分将会极大地提高你的生产力。 也许没有这一节内容的帮助你也能写出高效的针对IoC应用的单元测试,但是以下将要提到的 Spring 所提供的 mock 对象和测试支持类,在一些特定的场景中会对单元测试很有帮助。 ## 10.1 Mock 对象 ## 10.1.1 环境 org.springframework.mock.env 包含了对抽象 Environment 和 PropertySource 的 Mock 实现(参考 3.13.1 节 “Bean的定义文件” 和 3.13.3 节 “PropertySource抽象”)。MockEnvironment 和 MockPropertySource 对于编写针对依赖于环境相关属性的代码的,与容器无关的测试用例很有帮助。 ## 10.1.2 JNDI org.springframework.mock.jndi 包含了JNDI SPI的实现。这一实现可以让你为你的测试套件或独立应用配置起一个简单的JNDI环境。例如,如果在测试代码和Java EE容器中JDBC的 DataSource 都绑定到同一个JNDI名字上,你就可以在测试中直接复用应用程序的代码和配置而不需做任何改动。 ## 10.1.3 Servlet API org.springframework.mock.web 包含了十分全面的Servlet API mock 对象。这些对象在测试web context,controllers 和 filters 的时候很有用。由于这些 mock 对象是有针对性地为了与 Spring 的 Web MVC 框架共同使用而编写的,因此相比起诸如 EasyMock 这种动态mock 对象或 MockObjects 这种替代性的 Servlet API mock 对象,使用起来要更为方便。 ## 10.2 单元测试支持类 ## 10.2.1 通用支持工具 org.springframework.test.util 包含了一些供单元测试和集成测试中使用的通用工具。 ReflectionTestUtils 是一组基于反射的方法集合。在测试包含如下一些测试用例的应用时,开发人员可以使用这些工具方法应对更改一个常量的值,设置一个非公有字段,调用一个非公有setter方法,或调用一个非公有的配置或生命周期回调方法等测试场景: 诸如JPA和Hibernate等广泛采用private或protected访问方式而非公有setter方法来访问domain entity属性的ORM(Object-Relational Mapping)框架 @Autowired,@Inject 和 @Resource 等用于对 private 或 protected 字段,setter 方法和配置方法进行依赖注入的 Spring 注解。 @PostConstruct 和 @PreDestroy 等用在生命周期回调方法上的注解。 AopTestUtils 是一组 AOP 相关工具方法的集合。这些方法可以用于帮助获取隐藏于一重或多重 Spring 代理之后目标对象的引用。举个栗子,你使用 EasyMock 或 Mockito mock 了一个被包装在 Spring 代理之中的 bean,这时你可能需要对这个 mock 进行直接的访问,从而能够配置对此 mock 的期望行为并在稍后执行验证。关于 Spring 提供的核心 AOP 工具,请参考 AopUtils 和 AopProxyUtils 这两个类。 ## 10.2.2 Spring MVC org.springframework.test.web 包含了 ModelAndViewAssert 类。你可以在 Junit,TestNG或用任何测试框架编写的单元测试中使用这个类来帮助你跟 Spring MVC 框架的 ModelAndView 对象进行互动。 如果想要像测试 POJO 一样来测试你的 Spring MVC 控制器,你可以将 ModelAndViewAssert 与 MockHttpServletRequest,MockHttpSession 等来自 Servlet API mocks 的 Mock 类结合使用。而假如是要把 Spring MVC 和 REST 控制器和对 Spring MVC 的WebApplicationContext 配置结合起来进行一番彻底的集成测试,请使用 Spring MVC Test Framework 。 ## 11.1 概述 能够在不需要部署到应用服务器或连接到其它企业基础服务的前提下做一些集成测试是很重要的。这将使你能够测试以下内容: Spring IoC容器上下文的正确装配。 使用JDBC或其它ORM工具访问数据。这将包括SQL语句、Hibernate查询和JPA实体映射的正确性等等这些内容。 Spring Framework在spring-test模块中为集成测试提供了强有力的支持。该Jar包的实际名字可能会包含发布版本号而且可能是org.springframework.test这样长的形式,这取决于你是从哪获得的(请参阅section on Dependency Management中的解释)。这个库包括了org.springframework.test包,其中包含了使用Spring容器进行集成测试的重要的类。这个测试不信赖于应用服务器和其它的发布环境。这些测试会比单元测试稍慢但比同类型的Selenium测试或信赖于发布到应用服务器的远程测试要快得多。 在Spring2.5及之后的版本,单元和集成测试支持是以注解驱动Spring TestContext框架这样的形式提供的。TestContext框架对实际使用的测试框架是不可知的的,因此可以使用包括 JUnit, TestNG等等许多测试手段。 ## 11.2 集成测试的目标 Spring的集成测试支持有以下几点主要目标: 管理各个测试执行之间的 Spring IoC容器缓存 提供 测试配置实例的依赖注入 提供适合集成测试的 事务管理 提供辅助开发人员编写集成测试的 具备Spring特性的基础类 下面几节将解释每个目标并提供实现和配置详情的链接。 ## 11.2.1 上下文管理和缓存 Spring TestContex框架对Spring ApplicationContext 和WebApplicationContext提供一致性加载并对它们进行缓存。对加载的上下文进行缓存提供支持是很重要的,因为启动时间是个问题——不是因为Spring自己的开销,而是被Spring容器初始化的对象需要时间去初始化。比如,一个拥有50到100个Hibernate映射文件的项目可能花费10到20秒的时间去载这些映射文件,在运行每一个测试工具中的测试用例之前都会引发开销并导致总体测试运行变缓慢,降低开发效率。 测试类通常声明一批XML的资源路径或者Groovy的配置元数据——通常在类路径中——或者是一批用于配置应用程序的注解类。这些路径或者类跟在web.xml或者其它用于生产部署的配置文件中指定的是一样的。 通常,一旦被加载过一次,ApplicationContext就将被用于每个测试中。因此启动开销在一次测试集中将只会引发一次,随后执行的测试将会快得多。在这里,“测试集”的意思是在同一个JVM的所有测试——比如说,对给定项目或者模块的一次Ant、Maven或者Gradle构建运行的所有测试。在不太可能的情况下,一个测试会破坏应用上下文并引起重新加载——比如,修改一个bean定义或者应用程序对象的状态——TestContex框架将被设置为在开始下个测试之前重新加载配置并重建应用上下文。 ## 11.2.2 测试配置的信赖注入 当TestContext框架加载你的应用程序上下文的时候,它将通过信赖注入有选择性地配置测试实例。这为使用你的应用程序上下文中的预配置bean来建立测试配置提供了一个很方便的机制。这里有一个很大的便处就是你可以在不同的测试场景中重复使用应用程序上下文(比如,配置基于Spring管理的对象图、事务代理、数据源等等),这样省去了为每个测试用例建立复杂的测试配置的必要。 举例说明,考虑这样一个场景,我们有一个类叫做HibernateTitleRepository,它实现了Title领域实体的数据访问逻辑。我们想编写集成测试来测试以下方面: Spring配置:总的来说,就是与HibernateTitleRepository配置有关的一切是否正确和存在? Hibernate映射文件:是否所有映射都正确,并且延迟加载的设置是否准备就绪? HibernateTitleRepository的逻辑:此类中的配置实例是否与预期一致? 查看使用TestContext框架进行测试配置的信赖注入。 ## 11.2.3 事务管理 测试中访问一个真实数据库的一个常见的问题是在持久层存储状态付出的努力。即使你使用开发环境的数据库,改变相应的状态也会影响将来的测试。并且,许多操作——插入或者改变持久层数据——也不能在事务之外执行(或者验证)。 TestContext框架解决了这个问题。默认行为下,这个框架将为每个测试创建并回滚一个事务。你只需简单的假定事务是存在的并写你的代码即可。如果你调用事务代理对象,他们也会根据它们配置的语义正确执行。而且,如果一个测试方法在运行相应事务时删除了选定表中的内容,事务默认情况下会进行回滚,数据库会回到测试执行前的那个状态。事务通过定义在应用程序上下文中的PlatformTransactionManager bean来得到支持。 如果你需要提交事务——通常不会这样做,但有时当你想用一个特定的测试来填充或者修改数据库时也会显得有用——TestContext框架将根据@Commit注解的指示对事务进行提交而不是回滚。 查看使用TestContext框架进行事务管理。 ## 11.2.3 集成测试的支持类 Spring TestContext框架提供了一些支持来简化集成测试的编写。这些基础类为测试框架提供了定义良好的钩子,还有一些便利的实例变量和方法,使你能够访问: ApplicationContext,用于从整体上来进行显示的bean查找或者测试上下文的状态。 JdbcTemplate,用于执行SQL语句来查询数据库。这些的查询可用于确认执行数据库相关的应用程序代码前后数据库的状态,并且Spring保证这些查询与应用程序代码在同一个事务作用域中执行。如果需要与ORM工具协同使用,请确保避免误报。 还有,你可能想用特定于你的项目的实例和方法来创建你自己自定义的,应用程序范围的超类。 查看TestContext框架的支持类。 ## 11.3 JDBC测试支持 org.springframework.test.jdbc是包含JdbcTestUtils的包,它是一个JDBC相关的工具方法集,意在简化标准数据库测试场景。特别地,JdbcTestUtils提供以下静态工具方法: countRowsInTable(..):统计给定表的行数。 countRowsInTableWhere(..):使用提供的where语句进行筛选统计给定表的行数。 deleteFromTables(..):删除特定表的全部数据。 deleteFromTableWhere(..):使用提供的where语句进行筛选并删除给定表的数据。 dropTables(..):删除指定的表。 注意AbstractTransactionalJUnit4SpringContextTests和AbstractTransactionalTestNGSpringContextTests提供了委托给前面所述的JdbcTestUtils中的方法的简便方法。 spring-jdbc模块提供了配置和启动嵌入式数据库的支持,可用于与数据库交互的集成测试中。 详见Section 15.8, “嵌入式数据库支持”和ection 15.8.5, “使用嵌入式数据库测试数据访问逻辑”。 ## 11.4 注解 ## 11.4.1 Spring测试注解 Spring框架提供以下Spring特定的注解集合,你可以在单元和集成测试中协同TestContext框架使用它们。请参考相应的JAVA帮助文档作进一步了解,包括默认的属性,属性别名等等。、 ## @BootstrapWith @BootstrapWith是一个用于配置Spring TestContext框架如何引导的类级别的注解。具体地说,@BootstrapWith用于指定一个自定义的TestContextBootstrapper。请查看引导TestContext框架作进一步了解。 ## @ContextConfiguration @ContextConfiguration定义了类级别的元数据来决定如何为集成测试来加载和配置应用程序上下文。具体地说,@ContextConfiguration声明了用于加载上下文的应用程序上下文资源路径和注解类。 资源路径通常是类路径中的XML配置文件或者Groovy脚本;而注解类通常是使用@Configuration注解的类。但是,资源路径也可以指向文件系统中的文件和脚本,解决类也可能是组件类等等。 @ContextConfiguration("/test-config.xml") public class XmlApplicationContextTests { // class body... } @ContextConfiguration(classes = TestConfig.class) public class ConfigClassApplicationContextTests { // class body... } 作为声明资源路径或注解类的替代方案或补充,@ContextConfiguration可以用于声明ApplicationContextInitializer类。 @ContextConfiguration(initializers = CustomContextIntializer.class) public class ContextInitializerTests { // class body... } @ContextConfiguration偶尔也被用作声明ContextLoader策略。但注意,通常你不需要显示的配置加载器,因为默认的加载器已经支持资源路径或者注解类以及初始化器。 @ContextConfiguration(locations = "/test-context.xml", loader = CustomContextLoader.class) public class CustomLoaderXmlApplicationContextTests { // class body... } @ContextConfiguration默认对继承父类定义的资源路径或者配置类以及上下文初始化器提供支持。 参阅Section 11.5.4, 上下文管理和@ContextConfiguration帮助文档作进一步了解。 ## @WebAppConfiguration @WebAppConfiguration是一个用于声明集成测试所加载的ApplicationContext须是WebApplicationContext的类级别的注解。测试类的@WebAppConfiguration注解只是为了保证用于测试的WebApplicationContext会被加载,它使用”file:src/main/webapp”路径默认值作为web应用的根路径(即,资源基路径)。资源基路径用于幕后创建一个MockServletContext作为测试的WebApplicationContext的ServletContext。 @ContextConfiguration @WebAppConfiguration("classpath:test-web-resources") public class WebAppTests { // class body... } 注意@WebAppConfiguration必须和@ContextConfiguration一起使用,或者在同一个测试类,或者在测试类层次结构中。请参阅@WebAppConfiguration帮助文档作进一步了解。 ## @ContextHierarchy @ContextHierarchy是一个用于为集成测试定义ApplicationContext层次结构的类级别的注解。@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... } 如果你想合并或者覆盖一个测试类的层次结构中的应用程序上下文中指定层次的配置,你就必须在类层次中的每一个相应的层次通过为@ContextConfiguration的name属性提供与该层次相同的值的方式来显示地指定这个层次。请参阅上下文层次关系和@ContextHierarchy帮助文档来获得更多的示例。 ## @ActiveProfiles @ActiveProfiles是一个用于当集成测试加载ApplicationContext的时候声明哪一个bean definition profiles被激活的类级别的注解。 @ContextConfiguration @ActiveProfiles("dev") public class DeveloperTests { // class body... } @ContextConfiguration @ActiveProfiles({"dev", "integration"}) public class DeveloperIntegrationTests { // class body... } @ActiveProfiles默认为继承激活的在超类声明的 bean definition profiles提供支持。通过实现一个自定义的 ActiveProfilesResolver并通过@ActiveProfiles的resolver属性来注册它的编程的方式来解决激活bean definition profiles问题也是可行的。 参阅使用环境profiles来配置上下文和@ActiveProfiles帮助文档作进一步了解。 参阅使用环境profiles来配置上下文和@ActiveProfiles帮助文档作进一步了解。 ## @TestPropertySource @TestPropertySource是一个用于为集成测试加载ApplicationContext时配置属性文件的位置和增加到Environment中的PropertySources集中的内联属性的类级别的注解。 测试属性源比那些从系统环境或者Java系统属性以及通过@PropertySource或者编程方式声明方式增加的属性源具有更高的优先级。而且,内联属性比从资源路径加载的属性具有更高的优先级。 下面的例子展示了如何从类路径中声明属性文件。 @ContextConfiguration @TestPropertySource("/test.properties") public class MyIntegrationTests { // class body... } 面的例子展示了如何声明内联属性。 @ContextConfiguration @TestPropertySource(properties = { “timezone = GMT”, “port: 4242” }) public class MyIntegrationTests { // class body… } @DirtiesContext @DirtiesContext指明测试执行期间该Spring应用程序上下文已经被弄脏(也就是说通过某种方式被更改或者破坏——比如,更改单例bean的状态)。当应用程序上下文被标为”脏”,它将从测试框架缓存中被移除并关闭。因此,Spring容器将为随后需要同样配置元数据的测试而被重建。 @DirtiesContext可以在同一个类或者类层次结构中的类级别和方法级别中使用。在这个场景下,应用程序上下文将在任意此注解的方法之前或之后以及当前测试类之前或之后被标为“脏”,这取决于配置的methodMode和classMode。 下面的例子解释了在多种配置场景下什么时候上下文会被标为“脏”。 当在一个类中声明并将类模式设为BEFORE_CLASS,则在当前测试类之前。 @DirtiesContext(classMode = BEFORE_CLASS) public class FreshContextTests { // some tests that require a new Spring container } 当在一个类中声明并将类模式设为AFTER_CLASS(也就是,默认的类模式),则在当前测试类之后。 @DirtiesContext public class ContextDirtyingTests { // some tests that result in the Spring container being dirtied } 当在一个类中声明并将类模式设为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,则在当前测试类的每个方法之后。 @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) public class ContextDirtyingTests { // some tests that result in the Spring container being dirtied } 当在一个方法中声明并将方法模式设为BEFORE_METHOD,则在当前方法之前。 @DirtiesContext(methodMode = BEFORE_METHOD) @Test public void testProcessWhichRequiresFreshAppCtx() { // some logic that requires a new Spring container } 当在一个方法中声明并将方法模式设为AFTER_METHOD(也就是说,默认的方法模式),则在当前方法之后。 @DirtiesContext @Test public void testProcessWhichDirtiesAppCtx() { // some logic that results in the Spring container being dirtied } 如果@DirtiesContext被用于上下文被配置为通过@ContextHierarchy定义的上下文层次中的一部分的测试中,则hierarchyMode标志可用于控制如何声明上下文缓存。默认将使用一个穷举算法用于清除包括不仅当前层次而且与当前测试拥有共同祖先的其它上下文层次的缓存。所有在拥有共同祖先上下文的子层次的应用程序上下文都会从上下文中被移除并关闭。如果穷举算法对于特定的使用场景显得有点威力过猛,那么你可以指定一个更简单的当前层算法来代替,如下所。 @ContextHierarchy({ @ContextConfiguration("/parent-config.xml"), @ContextConfiguration("/child-config.xml") }) public class BaseTests { // class body... } public class ExtendedTests extends BaseTests { @Test @DirtiesContext(hierarchyMode = CURRENT_LEVEL) public void test() { // some logic that results in the child context being dirtied } } 参阅DirtiesContext.HierarchyMode帮助文档以获得穷举和当前层算法更详细的了解。 ## @TestExecutionListeners @TestExecutionListeners定义了一个类级别的元数据,用于配置需要用TestContextManager进行注册的TestExecutionListener实现。通常,@TestExecutionListeners与@ContextConfiguration一起使用。 @ContextConfiguration @TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class}) public class CustomTestExecutionListenerTests { // class body... } @TestExecutionListeners默认支持继承监听器。参阅帮助文档获得示例和更详细的了解。 ## @Commit @Commit指定事务性的测试方法在测试方法执行完成后对事务进行提交。@Commit可以用作@Rollback(false)的直接替代,以更好的传达代码的意图。和@Rollback一样,@Commit可以在类层次或者方法层级声明。 @Commit @Test public void testProcessWithoutRollback() { // ... } @Rollback @Rollback指明当测试方法执行完毕的时候是否对事务性方法中的事务进行回滚。如果为true,则进行回滚;否则,则提交(请参加@Commit)。在Spring TestContext框架中,集成测试默认的Rollback语义为true,即使你不显示的指定它。 当被声明为方法级别的注解,则@Rollback为特定的方法指定回滚语义,并覆盖类级别的@Rollback和@Commit语义。 @Rollback(false) @Test public void testProcessWithoutRollback() { // … } @BeforeTransaction @BeforeTransaction指明通过Spring的@Transactional注解配置为需要在事务中执行的测试方法在事务开始之前先执行注解的void方法。从Spring框架4.3版本起,@BeforeTransaction方法不再需要为public并可能被声明为基于Java8的接口的默认方法。 @BeforeTransaction void beforeTransaction() { // logic to be executed before a transaction is started } @AfterTransaction @AfterTransaction指明通过Spring的@Transactional注解配置为需要在事务中执行的测试方法在事务结束之后执行注解的void方法。从Spring框架4.3版本起,@AfterTransaction方法不再需要为public并可能被声明为基于Java8的接口的默认方法。 @AfterTransaction void afterTransaction() { // logic to be executed after a transaction has ended } @Sql @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 } 请参阅通过@sql声明执行的SQL脚本作进一步了解。 ## @SqlConfig @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是一个用于聚合几个@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 } 11.4.2 标准注解支持 以下注解为Spring TestContext 框架所有的配置提供标准语义支持。注意这些注解不仅限于测试,可以用在Spring框架的任意地方。 @Autowired @Qualifier @Resource(javax.annotation)如果JSR-250存在 @ManagedBean(javax.annotation)如果JSR-250存在 @Inject(javax.inject)如果JSR-330存在 @Named(javax.inject)如果JSR-330存在 @PersistenceContext(javax.persistence)如果JPA存在 @PersistenceUnit(javax.persistence)如果JPA存在 @Required @Transactional 在Spring TestContext 框架中,@PostConstruct 和 @PreDestroy 可以通过标准语义在配置于应用程序上下文的任意应用程序组件中使用; 但是, 这些生命周期注解在实际测试类中只有很有限的作用。如果一个测试类的方法被注解为@PostConstruct,这个方法将在test框架中的任何before方法(也就是被JUnit中的@Before注解方法)调用之前被执行, 这个规则将被应用于测试类的每个方法。另一方面,如果一个测试类的方法被注解为 @PreDestroy,这个方法将永远不会被执行。因为建议在测试类中使用test 框架的测试生命周期回调来代替使用@PostConstruct and @PreDestroy。 ## 11.4.3 Spring JUnit 4 测试注解 @IfProfileValue指明该测试只在特定的测试环境中被启用。如果ProfileValueSource配置的name属性与此注解配置的name属性一致,这该测试将被启用。否则,该测试将被禁用并忽略。 @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 } 或者,你可以 配置@IfProfileValue使用values列表(或语义)来实现JUnit 4环境中的类似TestNG对测试组的支持。 @IfProfileValue(name="test-groups", values={"unit-tests", "integration-tests"}) @Test public void testProcessWhichRunsForUnitOrIntegrationTestGroups() { // some logic that should run only for unit and integration test groups } @ProfileValueSourceConfiguration @ProfileValueSourceConfiguration是类级别注解,用于当获取通过@IfProfileValue配置的profile值时指定使用什么样的ProfileValueSource类型。如果一个测试没有指定@ProfileValueSourceConfiguration,那么默认使用SystemProfileValueSource。 @ProfileValueSourceConfiguration(CustomProfileValueSource.class) public class CustomProfileValueSourceTests { // class body... } @Timed @Timed用于指明被注解的测试必须在指定的时限(毫秒)内结束。如果测试超过指定时限,就当作测试失败。 时限包括测试方法本身所耗费的时间,包括任何重复(请查看@Repeat)及任意初始化和销毁所用的时间。 @Timed(millis=1000) public void testProcessWithOneSecondTimeout() { // some logic that should not take longer than 1 second to execute } Spring的@Timed注解与JUnit 4的@Test(timeout=…)支持相比具有不同的语义。确切地说,由于在JUnit 4中处理方法执行超时的方式(也就是,在独立纯程中执行该测试方法),如果一个测试方法执行时间太长,@Test(timeout=…)将直接判定该测试失败。而Spring的@Timed则不直接判定失败而是等待测试完成。 ## @Repeat @Repeat指明该测试方法需被重复执行。注解指定该测试方法被重复的次数。重复的范围包括该测试方法自身也包括相应的初始化和销毁方法。 @Repeat(10) @Test public void testProcessRepeatedly() { // ... } 11.4.4 Meta-Annotation Support for Testing 可以将大部分测试相关的注解当作meta-annotations使用,以创建自定义组合注解来减少测试集中的重复配置。 下面的每个都可以在TestContext框架中被当作meta-annotations使用。 @BootstrapWith @ContextConfiguration @ContextHierarchy @ActiveProfiles @TestPropertySource @DirtiesContext @WebAppConfiguration @TestExecutionListeners @Transactional @BeforeTransaction @AfterTransaction @Commit @Rollback @Sql @SqlConfig @SqlGroup @Repeat @Timed @IfProfileValue @ProfileValueSourceConfiguration 例如,如果发现我们在基于JUnit 4的测试集中重复以下配置… @RunWith(SpringRunner.class) @ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"}) @ActiveProfiles("dev") @Transactional public class OrderRepositoryTests { } @RunWith(SpringRunner.class) @ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"}) @ActiveProfiles("dev") @Transactional public class UserRepositoryTests { } 我们可以通过一个自定义的组合注解来减少上述的重复量,将通用的测试配置集中起来,就像这样: @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"}) @ActiveProfiles("dev") @Transactional public @interface TransactionalDevTest { } 然后我们就可以像下面一样使用我们自定义的@TransactionalDevTest注解来简化每个类的配置: @RunWith(SpringRunner.class) @TransactionalDevTest public class OrderRepositoryTests { } @RunWith(SpringRunner.class) @TransactionalDevTest public class UserRepositoryTests { } 想获得详情,请查看Spring注解编程模型 ## 14. DAO支持 ## 14.1 介绍 在Spring中数据访问对象(DAO)旨在使JDBC,Hibernate,JPA或JDO等数据访问技术有一致的处理方法,并且方法尽可能简单。 这样就可以很容易地切换上述持久化技术,并且切换过程无需担心每种技术的特有异常。 ## 14.2 一致的异常层级 Spring为技术特定异常提供了一个适当的转化,例如:SQLException 所属异常类层级用DataAccessException 作为根异常,这些异常包括了原来的异常,所以不会有丢失可能出错信息的风险。 除了JDBC异常,Spring也包含了Hibernate特定异常,将它们转换为一组集中的运行时异常(对JDO 和 JPA 异常也是如此),在合适的层次上处理多数不可恢复的持久化异常,而不会在dao上产生繁琐的catch-throw块和异常声明(仍然可以在认为合适的地方捕获和处理异常)。向上面提到的一样,JDBC异常(包括数据方言)也都转化为相同的层级结构,意味着在一个统一的项目模型中你也可以执行一些JDBC操作。 以上列举的Spring的各种模板类支持各种ORM框架。如果使用基于拦截器的类,那么我们的程序必须关心并处理HibernateExceptions和JDOExceptions本身,最好是通过分别授权给SessionFactoryUtils’ \convertHibernateAccessException(..)或 convertJdoAccessException()方法。这些方法将这些异常转化为与org.springframework.dao中异常层级兼容的异常。由于JDOExceptions 没有被检查,它可以被简单的抛出,这也牺牲了DAO在异常上的抽象。 下图展示了Spring提供的异常层级,(请注意:在这张图上显示出来的类层级仅仅是整个DataAccessException 的一个子集) ## 14.3 配置 DAO 或 Repository 类的注解 14.3 配置 DAO 或 Repository 类的注解** ## 使用@Repository注解是数据访问对象(DAOs)或库能提供异常转换的最好方式,这个注解还允许组件扫描,查找并配置你的 DAOs 和库,并且不需要为它们提供 XML 配置文件。 @Repository public class SomeMovieFinder implements MovieFinder { // ... } 任何DAO或库实现都需要访问持久的源,依赖于持久化技术的使用;例如:一个基于JDBC的库需要访问一个JDBC DataSource,一个基于JPA的库需要访问一个 EntityManager,最简单的方式就是使用 @Autowired, @Inject, @Resource 或@PersistenceContext 这些注解中的一个完成资源的依赖注入,这是一个JPA库的例子: @Repository public class JpaMovieFinder implements MovieFinder { @PersistenceContext private EntityManager entityManager; // ... } 如果你使用传统的Hibernate API,你可以注入SessionFactory: @Repository public class HibernateMovieFinder implements MovieFinder { private SessionFactory sessionFactory; @Autowired public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } // ... } 最后一个例子我们将在这里展示典型的JDBC支持,你将会在初始化方法中注入 DataSource ,在初始化方法中,你将使用这个DataSource创建一个JdbcTemplate 和其他与SimpleJdbcCall相似的数据访问支持类。 @Repository public class JdbcMovieFinder implements MovieFinder { private JdbcTemplate jdbcTemplate; @Autowired public void init(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } // ... } --- *本文基于DDKK.COM的Spring教程系列进行二次创作* *原文链接:https://ddkk.com/zhuanlan/j2ee/spring/1/40.html*