目录 搜索
前言 简介 概览 使用场景 Spring 2.0和 2.5的新特性 简介 控制反转(IoC)容器 新的bean作用域 更简单的XML配置 可扩展的XML编写 Annotation(注解)驱动配置 在classpath中自动搜索组件 面向切面编程(AOP) 更加简单的AOP XML配置 对@AspectJ 切面的支持 对bean命名pointcut( bean name pointcut element)的支持 对AspectJ装载时织入(AspectJ load-time weaving)的支持 中间层 在XML里更为简单的声明性事务配置 对Websphere 事务管理的完整支持 JPA 异步的JMS JDBC Web层 Spring MVC合理的默认值 Portlet 框架 基于Annotation的控制器 Spring MVC的表单标签库 对Tiles 2 支持 对JSF 1.2支持 JAX-WS支持 其他 动态语言支持 增强的测试支持 JMX 支持 将Spring 应用程序上下文部署为JCA adapter 计划任务 对Java 5 (Tiger) 支持 移植到Spring 2.5 改变 支持的JDK版本 Spring 2.5的Jar打包 XML配置 Deprecated(淘汰)的类和方法 Apache OJB iBATIS Hibernate JDO UrlFilenameViewController 更新的样例应用 改进的文档 核心技术 IoC(控制反转)容器 简介 基本原理 - 容器和bean 容器 配置元数据 实例化容器 XML配置元数据的结构 多种bean bean的命名 bean的别名 实例化bean 用构造器来实例化 使用静态工厂方法实例化 使用实例工厂方法实例化 使用容器 依赖 注入依赖 构造器注入 构造器参数解析 构造器参数类型匹配 构造参数索引 Setter注入 一些例子 依赖配置详解 直接变量(基本类型、Strings类型等。) idref元素 引用其它的bean(协作者) 内部bean 集合 集合的合并 强类型集合(仅适用于Java5+) Nulls XML配置文件的简写及其他 XML-based configuration metadata shortcuts 使用p名称空间配置属性 组合属性名称 使用depends-on 延迟初始化bean 自动装配(autowire)协作者 将bean排除在自动装配之外 依赖检查 方法注入 Lookup方法注入 自定义方法的替代方案 Bean的作用域 Singleton作用域 Prototype作用域 Singleton beans和prototype-bean的依赖 其他作用域 初始化web配置 Request作用域 Session作用域 global session作用域 作用域bean与依赖 选择创建代理的类型 自定义作用域 创建自定义作用域 使用自定义作用域 定制bean特性 生命周期回调 初始化回调 析构回调 缺省的初始化和析构方法 组合生命周期机制 在非web应用中优雅地关闭Spring IoC容器 了解自己 BeanFactoryAware BeanNameAware bean定义的继承 容器扩展点 用BeanPostProcessor定制bean 使用BeanPostProcessor的Hello World示例 RequiredAnnotationBeanPostProcessor示例 用BeanFactoryPostProcessor定制配置元数据 PropertyPlaceholderConfigurer示例 PropertyOverrideConfigurer示例 使用FactoryBean定制实例化逻辑 The ApplicationContext BeanFactory 还是 ApplicationContext? 利用MessageSource实现国际化 事件 底层资源的访问 ApplicationContext在WEB应用中的实例化 粘合代码和可怕的singleton 以J2EE RAR文件的形式部署Spring ApplicationContext 基于注解(Annotation-based)的配置 @Autowired 基于注解的自动连接微调 CustomAutowireConfigurer @Resource @PostConstruct 与 @PreDestroy 对受管组件的Classpath扫描 @Component和更多典型化注解 自动检测组件 使用过滤器自定义扫描 自动检测组件的命名 为自动检测的组件提供一个作用域 用注解提供限定符元数据 注册一个LoadTimeWeaver 资源 简介 Resource接口 内置 Resource 实现 UrlResource ClassPathResource FileSystemResource ServletContextResource InputStreamResource ByteArrayResource ResourceLoader接口 ResourceLoaderAware 接口 把Resource作为属性来配置 Application context 和Resource 路径 构造application context 创建 ClassPathXmlApplicationContext 实例 - 简介 Application context构造器中资源路径的通配符 Ant风格的pattern 潜在的可移植性 classpath*: 前缀 其他关于通配符的说明 FileSystemResource 说明 校验,数据绑定,BeanWrapper,与属性编辑器 简介 使用Spring的Validator接口进行校验 从错误代码到错误信息 Bean处理和BeanWrapper 设置和获取属性值以及嵌套属性 内建的PropertyEditor实现 注册用户自定义的PropertyEditor 使用PropertyEditorRegistrars 使用Spring进行面向切面编程(AOP) 简介 AOP概念 Spring AOP的功能和目标 AOP代理 @AspectJ支持 启用@AspectJ支持 声明一个切面 声明一个切入点(pointcut) 切入点指示符(PCD)的支持 组合切入点表达式 共享通用切入点定义 示例 声明通知 前置通知 后置通知(After returning advice) 异常通知(After throwing advice) 最终通知(After (finally) advice) 环绕通知 通知参数(Advice parameters) 访问当前的连接点 传递参数给通知 确定参数名 处理参数 通知顺序 引入(Introduction) 切面实例化模型 例子 基于Schema的AOP支持 声明一个切面 声明一个切入点 声明通知 前置通知 后置通知 异常通知 最终通知 环绕通知 通知参数 通知顺序 引入 切面实例化模型 Advisor 例子 AOP声明风格的选择 Spring AOP还是完全用AspectJ? Spring AOP中使用@AspectJ还是XML? 混合切面类型 代理机制 理解AOP代理 以编程方式创建@AspectJ代理 在Spring应用中使用AspectJ 在Spring中使用AspectJ进行domain object的依赖注入 @Configurable对象的单元测试 Working with multiple application contexts Spring中其他的AspectJ切面 使用Spring IoC来配置AspectJ的切面 在Spring应用中使用AspectJ加载时织入(LTW) 第一个例子 切面 'META-INF/aop.xml' 相关类库(JARS) Spring配置 特定环境的配置 通用Java应用 Tomcat WebLogic 更多资源 Spring AOP APIs 简介 Spring中的切入点API 概念 切入点运算 AspectJ切入点表达式 便利的切入点实现 静态切入点 正则表达式切入点 属性驱动的切入点 动态切入点 控制流切入点 切入点的超类 自定义切入点 Spring的通知API 通知的生命周期 Spring里的通知类型 拦截环绕通知 前置通知 异常通知 后置通知 引入通知 Spring里的Advisor API 使用ProxyFactoryBean创建AOP代理 基础 JavaBean属性 基于JDK和CGLIB的代理 对接口进行代理 对类进行代理 使用“全局”通知器 简化代理定义 使用ProxyFactory通过编程创建AOP代理 操作被通知对象 使用“自动代理(autoproxy)”功能 自动代理bean定义 BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator AbstractAdvisorAutoProxyCreator 使用元数据驱动的自动代理 使用TargetSource 热交换目标源 池化目标源 原型目标源 ThreadLocal目标源 定义新的Advice类型 更多资源 测试 简介 单元测试 Mock对象 JNDI Servlet API Portlet API 单元测试支持类 通用工具类 Spring MVC 集成测试 概览 使用哪个支持框架 通用目标 上下文管理及缓存 测试fixtures依赖注入 事务管理 集成测试支持类 JDBC测试支持 常用注解 JUnit 3.8遗留支持 上下文管理及缓存 测试fixture依赖注入 字段级别(Field Level)注入 事务管理 JUnit 3.8 遗留支持类 Java 5+ 专有支持 使用注解的事务相关测试 JPA支持类 Spring TestContext Framework 主要的抽象 上下文管理和缓存 测试fixture的依赖注入 事务管理 TestContext支持类 JUnit 3.8支持类 JUnit 4.4支持类 定制JUnit 4.4运行器 TestNG支持类 TestContext框架注解支持 PetClinic示例 更多资源 中间层数据访问 事务管理 简介 动机 关键抽象 使用资源同步的事务 高层次方案 低层次方案 TransactionAwareDataSourceProxy 声明式事务管理 理解Spring的声明式事务管理实现 第一个例子 回滚 为不同的bean配置不同的事务语义 <tx:advice/> 有关的设置 使用 @Transactional @Transactional 有关的设置 事务传播 required RequiresNew Nested 通知事务操作 结合AspectJ使用 @Transactional 编程式事务管理 使用TransactionTemplate 指定事务设置 使用PlatformTransactionManager 选择编程式事务管理还是声明式事务管理 与特定应用服务器集成 IBM WebSphere BEA WebLogic Oracle OC4J 常见问题的解决方法 对一个特定的 DataSource 使用了错误的事务管理器 更多的资源 DAO支持 简介 一致的异常层次 一致的DAO支持抽象类 使用JDBC进行数据访问 简介 选择一种工作模式 Spring JDBC包结构 利用JDBC核心类控制JDBC的基本操作和错误处理 JdbcTemplate类 一些示例 查询(SELECT) 更新(INSERT/UPDATE/DELETE) 其他操作 JdbcTemplate 的最佳实践 NamedParameterJdbcTemplate类 SimpleJdbcTemplate类 DataSource接口 SQLExceptionTranslator接口 执行SQL语句 执行查询 更新数据库 获取自动生成的主键 控制数据库连接 DataSourceUtils类 SmartDataSource接口 AbstractDataSource类 SingleConnectionDataSource类 DriverManagerDataSource类 TransactionAwareDataSourceProxy类 DataSourceTransactionManager类 NativeJdbcExtractor JDBC批量操作 使用JdbcTemplate进行批量操作 使用SimpleJdbcTemplate进行批量操作 通过使用SimpleJdbc类简化JDBC操作 使用SimpleJdbcInsert插入数据 使用SimpleJdbcInsert来获取自动生成的主键 指定SimpleJdbcInsert所使用的字段 使用SqlParameterSource提供参数值 使用SimpleJdbcCall调用存储过程 声明SimpleJdbcCall使用的参数 如何定义SqlParameters 使用SimpleJdbcCall调用内置函数 使用SimpleJdbcCall返回的ResultSet/REF Cursor 用Java对象来表达JDBC操作 SqlQuery类 MappingSqlQuery类 SqlUpdate类 StoredProcedure类 SqlFunction类 参数和数据处理的基本原则 为参数设置SQL类型信息 处理BLOB 和 CLOB对象 在IN语句中传入一组参数值 处理复杂类型的存储过程调用 使用ORM工具进行数据访问 简介 Hibernate 资源管理 在Spring容器中创建 SessionFactory The HibernateTemplate 不使用回调的基于Spring的DAO实现 基于Hibernate3的原生API实现DAO 编程式的事务划分 声明式的事务划分 事务管理策略 容器资源 vs 本地资源 在应用服务器中使用Hibernate的注意事项 JDO 建立PersistenceManagerFactory JdoTemplate和JdoDaoSupport 基于原生的JDO API实现DAO 事务管理 JdoDialect Oracle TopLink SessionFactory 抽象层 TopLinkTemplate and TopLinkDaoSupport 基于原生的TopLink API的DAO实现 事务管理 iBATIS SQL Maps 创建SqlMapClient 使用 SqlMapClientTemplate 和 SqlMapClientDaoSupport 基于原生的iBATIS API的DAO实现 JPA 在Spring环境中建立JPA LocalEntityManagerFactoryBean 从JNDI中获取 EntityManagerFactory LocalContainerEntityManagerFactoryBean Tomcat(5.0以上)加载时的织入配置 使用VM代理的全局加载时织入 上下文范围内的加载时织入配置 处理多持久化单元 JpaTemplate 和 JpaDaoSupport 基于原生的JPA实现DAO 异常转化 事务管理 JpaDialect The Web Web MVC framework Web框架 概述 与其他MVC实现框架的集成 Spring Web MVC框架的特点 DispatcherServlet 控制器 AbstractController 和 WebContentGenerator 其它的简单控制器 MultiActionController 命令控制器 处理器映射(handler mapping) BeanNameUrlHandlerMapping SimpleUrlHandlerMapping 拦截器(HandlerInterceptor) 视图与视图解析 视图解析器(ViewResolver) 视图解析链 重定向(Rediret)到另一个视图 RedirectView redirect:前缀 forward:前缀 本地化解析器 AcceptHeaderLocaleResolver CookieLocaleResolver SessionLocaleResolver LocaleChangeInterceptor 使用主题 简介 如何定义主题 主题解析器 Spring对分段文件上传(multipart file upload)的支持 介绍 使用MultipartResolver 在表单中处理分段文件上传 使用Spring的表单标签库 配置 form标签 input标签 checkbox标签 checkboxes标签 radiobutton标签 radiobuttons标签 password标签 select标签 option标签 options标签 textarea标签 hidden标签 errors标签 处理异常 惯例优先原则(convention over configuration) 对控制器的支持:ControllerClassNameHandlerMapping 对模型的支持:ModelMap(ModelAndView) 对视图的支持:RequestToViewNameTranslator 基于注解的控制器配置 建立dispatcher实现注解支持 使用@Controller定义一个控制器 使用@RequestMapping映射请求 使用@RequestParam绑定请求参数到方法参数 使用@ModelAttribute提供一个从模型到数据的链接 使用@SessionAttributes指定存储在会话中的属性 自定义WebDataBinder初始化 使用@InitBinder自定义数据绑定 配置一个定制的WebBindingInitializer 更多资源 集成视图技术 简介 JSP和JSTL 视图解析器 'Plain-old' JSPs versus JSTL 'Plain-old' JSP与JSTL 帮助简化开发的额外的标签 Tiles 需要的资源 如何集成Tiles UrlBasedViewResolver类 ResourceBundleViewResolver类 SimpleSpringPreparerFactory 和 SpringBeanPreparerFactory Velocity和FreeMarker 需要的资源 Context 配置 创建模板 高级配置 velocity.properties FreeMarker 绑定支持和表单处理 用于绑定的宏 简单绑定 表单输入生成宏 输入域 选择输入域 重载HTML转码行为并使你的标签符合XHTML XSLT 写在段首 Bean 定义 标准MVC控制器代码 把模型数据转化为XML 定义视图属性 文档转换 小结 文档视图(PDF/Excel) 简介 配置和安装 文档视图定义 Controller 代码 Excel视图子类 PDF视图子类 JasperReports 依赖的资源 配置 配置ViewResolver 配置View 关于报表文件 使用 JasperReportsMultiFormatView 构造ModelAndView 使用子报表 配置子报表文件 配置子报表数据源 配置Exporter的参数 集成其它Web框架 简介 通用配置 JavaServer Faces DelegatingVariableResolver FacesContextUtils Struts ContextLoaderPlugin DelegatingRequestProcessor DelegatingActionProxy ActionSupport Classes Tapestry 注入 Spring 托管的 beans 将 Spring Beans 注入到 Tapestry 页面中 组件定义文件 添加抽象访问方法 将 Spring Beans 注入到 Tapestry 页面中 - Tapestry 4.0+ 风格 WebWork 更多资源 Portlet MVC框架 介绍 控制器 - MVC中的C 视图 - MVC中的V Web作用范围的Bean DispatcherPortlet ViewRendererServlet 控制器 AbstractController 和 PortletContentGenerator 其它简单的控制器 Command控制器 PortletWrappingController 处理器映射 PortletModeHandlerMapping ParameterHandlerMapping PortletModeParameterHandlerMapping 增加 HandlerInterceptors HandlerInterceptorAdapter ParameterMappingInterceptor 视图和它们的解析 Multipart文件上传支持 使用 PortletMultipartResolver 处理表单里的文件上传 异常处理 Portlet应用的部署 整合 使用Spring进行远程访问与Web服务 简介 使用RMI暴露服务 使用RmiServiceExporter暴露服务 在客户端链接服务 使用Hessian或者Burlap通过HTTP远程调用服务 为Hessian和co.配置DispatcherServlet 使用HessianServiceExporter暴露你的bean 在客户端连接服务 使用Burlap 对通过Hessian或Burlap暴露的服务使用HTTP Basic认证 使用HTTP调用器暴露服务 Exposing the service object 在客户端连接服务 Web Services 使用JAX-RPC暴露基于servlet的web服务 使用JAX-RPC访问web服务 注册JAX-RPC Bean映射 注册自己的JAX-RPC 处理器 使用JAX-WS暴露基于servlet的web服务 使用JAX-WS暴露单独web服务 使用Spring支持的JAX-WS RI来暴露服务 使用JAX-WS访问web服务 使用XFire来暴露Web服务 JMS 服务端配置 客户端配置 对远程接口不提供自动探测实现 在选择这些技术时的一些考虑 Enterprise Java Beans (EJB) 集成 简介 访问EJB 概念 访问本地的无状态Session Bean(SLSB) 访问远程SLSB Accessing EJB 2.x SLSBs versus EJB 3 SLSBs 使用Spring提供的辅助类实现EJB组件 EJB 2.x base classes EJB 3 注入拦截 JMS (Java Message Service) 简介 使用Spring JMS JmsTemplate 连接工厂 目的地管理 消息侦听容器 SimpleMessageListenerContainer DefaultMessageListenerContainer ServerSessionMessageListenerContainer 事务管理 发送消息 使用消息转换器 SessionCallback 和 ProducerCallback 接收消息 同步接收 异步接收 - 消息驱动的POJO SessionAwareMessageListener接口 MessageListenerAdapter 事务中的消息处理 JCA消息端点的支持 JMS命名空间支持 JMX 介绍 将Bean暴露为JMX 创建MBeanServer 重用原有的MBeanServer 延迟初始化的MBean MBean的自动注册 控制注册行为 控制Bean的管理接口 MBeanInfoAssembler接口 使用源码级元数据 使用JDK 5.0的注解 源代码级的元数据类型 AutodetectCapableMBeanInfoAssembler接口 用Java接口定义管理接口 使用MethodNameBasedMBeanInfoAssembler 控制Bean的ObjectName 从Properties读取Properties 使用MetadataNamingStrategy <context:mbean-export/>元素 JSR-160连接器 服务器端连接器 客户端连接器 基于Burlap/Hessian/SOAP的JMX 通过代理访问MBean 通知 为通知注册监听器 发布通知 更多资源 JCA CCI 简介 配置CCI 连接器配置 在Spring中配置ConnectionFactory 配置CCI连接 使用一个 CCI 单连接 使用Spring的 CCI访问支持 记录转换 CciTemplate类 DAO支持 自动输出记录生成 总结 直接使用一个CCI Connection接口和Interaction接口 CciTemplate 使用示例 建模CCI访问为操作对象 MappingRecordOperation MappingCommAreaOperation 自动生成输出记录 总结 MappingRecordOperation 使用示例 MappingCommAreaOperation 使用示例 事务 Spring邮件抽象层 简介 使用Spring邮件抽象 MailSender 和 SimpleMailMessage 的基本用法 使用 JavaMailSender 和 MimeMessagePreparator 使用MimeMessageHelper 发送附件和嵌入式资源(inline resources) 附件 内嵌资源 使用模板来创建邮件内容 一个基于Velocity的示例 Spring中的定时调度(Scheduling)和线程池(Thread Pooling) 简介 使用OpenSymphony Quartz 调度器 使用JobDetailBean 使用 MethodInvokingJobDetailFactoryBean 使用triggers和SchedulerFactoryBean来包装任务 使用JDK Timer支持类 创建定制的timers 使用 MethodInvokingTimerTaskFactoryBean类 最后:使用TimerFactoryBean来设置任务 SpringTaskExecutor抽象 TaskExecutor接口 TaskExecutor类型 使用TaskExecutor 动态语言支持 介绍 第一个示例 定义动态语言支持的bean 公共概念 <lang:language/> 元素 Refreshable bean 内置动态语言源文件 理解dynamic-language-backed bean上下文中的构造器注入 JRuby beans Groovy beans 通过回调定制Groovy对象 BeanShell beans 场景 Spring MVC控制器的脚本化 Validator的脚本化 Bits and bobs AOP - 通知脚本化bean 作用域 更多的资源 注解和源代码级的元数据支持 简介 Spring的元数据支持 注解 @Required Spring中的其它@Annotations Jakarta Commons Attributes集成 元数据和Spring AOP自动代理 基本原理 声明式事务管理 示例程序 演示案例 介绍 使用动态语言实现的Spring MVC控制器 构建与部署 使用SimpleJdbcTemplate和@Repository实现DAO 域对象 Data Access Object 构建 XML Schema-based configuration Introduction XML Schema-based configuration Referencing the schemas The util schema <util:constant/> Setting a bean property or constructor arg from a field value <util:property-path/> Using <util:property-path/> to set a bean property or constructor-argument <util:properties/> <util:list/> <util:map/> <util:set/> The jee schema <jee:jndi-lookup/> (simple) <jee:jndi-lookup/> (with single JNDI environment setting) <jee:jndi-lookup/> (with multiple JNDI environment settings) <jee:jndi-lookup/> (complex) <jee:local-slsb/> (simple) <jee:local-slsb/> (complex) <jee:remote-slsb/> The lang schema The jms schema The tx (transaction) schema The aop schema The context schema <property-placeholder/> <annotation-config/> <component-scan/> <load-time-weaver/> <spring-configured/> <mbean-export/> The tool schema The beans schema Setting up your IDE Setting up Eclipse Setting up IntelliJ IDEA Integration issues XML parsing errors in the Resin v.3 application server Extensible XML authoring Introduction Authoring the schema Coding a NamespaceHandler Coding a BeanDefinitionParser Registering the handler and the schema 'META-INF/spring.handlers' 'META-INF/spring.schemas' Using a custom extension in your Spring XML configuration Meatier examples Nesting custom tags within custom tags Custom attributes on 'normal' elements Further Resources spring-beans-2.0.dtd spring.tld Introduction The bind tag The escapeBody tag The hasBindErrors tag The htmlEscape tag The message tag The nestedPath tag The theme tag The transform tag spring-form.tld Introduction The checkbox tag The checkboxes tag The errors tag The form tag The hidden tag The input tag The label tag The option tag The options tag The password tag The radiobutton tag The radiobuttons tag The select tag The textarea tag Spring 2.5开发手册中文化项目 声明 致谢 参与人员 项目历程
文字

8.3. 集成测试

8.3.1. 概览

能够无需部署到你的应用服务器上或连接其它企业架构就实现集成测试是非常重要的。这可以让你来进行以下测试:

  • 正确配置Spring IoC 容器上下文。

  • 使用JDBC或ORM工具的数据访问。可能包括如SQL脚本,Hibernate query,JPA 实体映射等的正确性验证。

Spring框架提供集成测试的一流支持,相关类打包在spring-test.jar类库中。在这个类库中,你可以找到org.springframework.test包,有很多方便使用Spring容器进行集成测试的类,而且同时不依赖应用服务器或其它部署环境。这些测试会比单元测试慢,但会比Cactus(译者注:Apache测试服务端Java代码的工具http://jakarta.apache.org/cactus/index.html )测试或依靠部署到一个应用服务器上来进行远程测试要快捷的多。

在2.5版本之前,Spring已经提供了面向JUnit 3.8的单元测试支持. 在2.5版本中, Spring 提供了单元和集成测试支持 Spring TestContext框架。 它是实际测试框架的混合体,因此能够帮助在多个测试环境包括JUnit 3.8,JUnit 4.4, TestNG等中进行测试。 注意Spring TestContext框架需要Java 5+支持.

8.3.2. 使用哪个支持框架

Spring团队推荐使用Spring TestContext框架 来进行所有新的单元测试和集成测试,以包括ApplicationContext或需要事务管理的情况。 但如果你开发在Java5之前的环境上,就需要继续使用JUnit 3.8遗留支持. 另外,显式 JPA集成测试支持 依赖于shadow class载入来进行JPA类测试(class instrumentation)目前只能与JUnit 3.8遗留支持相容。 如果你要测试的JPA提供者不需要class instrumentation,就推荐使用TestContext框架。

8.3.3. 通用目标

Spring集成测试支持框架提供了一些通用目标,包括:

  • 跨越各个测试案例执行期的Spring IoC容器缓存。

  • 测试fixture实例的依赖注入 (这很爽)。

  • 适合集成测试的事务管理(这更加爽)。

  • Spring特有的支持类在编写集成测试时真的很有用。

下面的章节具体描述每一个目标并提供指向特定支持框架的信息的链接。

8.3.3.1. 上下文管理及缓存

Spring集成测试支持框架提供了ApplicationContext的持久化载入和这些上下文的缓存机制。 对已载入上下文的缓存是很重要的,因为如果你是在一个大型的项目中,启动时间会成为一个问题――不是因为Spring本身的开销, 而是因为靠Spring容器来初始化的对象需要很长时间。比如一个有50-100 Hibernate映射文件的项目可能需要10-20秒来载入映射文件, 而每次单一测试fixture的每个单一测试前都要这样的时间开销,减慢了整体的测试进度进而降低效率。

测试类通常会提供一个数组来包含XML配置元数据的资源路径――通常是classpath――来配置应用。这通常和web.xml或其它部署描述中指定的配置路径是相同或相近的。

默认情况下,一旦载入,ApplicationContext将在每次测试中重用。 这样启动的开销将只需要一次(每个测试fixture),接下来的测试执行就会快得多。 在一些少见的会“污染”应用上下文的案例中需要重新载入―― 例如,改变一个bean定义或应用对象的状态―― Spring的测试支持提供了在执行下一个测试前让测试fixture重新载入配置并重建应用上下文的机制。

上下文管理和缓存使用:

  • JUnit 3.8遗留支持

  • TestContext框架

8.3.3.2. 测试fixtures依赖注入

当Spring集成测试支持框架载入你的应用上下文时,它们能通过依赖注入选择性配置测试类实例。 这提供了一个方便的机制来使用预先在应用上下文中配置的bean来搭建测试fixture。 很大的好处就是你可以在各种测试场景中重用应用上下文(例如配置Spring管理的对象图, 事务代理DataSource等),从而能避免为单个的测试案例重复进行测试fixture搭建。

作为例子,考虑一个场景:我们有一个HibernateTitleDao类来实现数据访问逻辑,假设是Title域对象。我们希望编写测试所有以下方面的集成测试:

  • Spring配置: 最基本的,是否所有与HibernateTitleDao bean相关的配置都是正确和存在的?

  • Hibernate映射配置文件: 是否所有映射都是正确的并且lazy-loading设置也到位了?

  • HibernateTitleDao逻辑:是否类的已配置示例的实现与预期相同?

测试fixtures依赖注入使用:

  • JUnit 3.8 遗留支持

  • TestContext框架

8.3.3.3. 事务管理

访问实际数据库的测试的一个通常问题是对持久化状态的影响。 即使你使用开发数据库,状态的改变也可能影响后面的测试。而且很多操作 ―― 如插入或修改持久化数据 ―― 不能在事务外完成(或验证)。

Spring集成测试支持框架满足了这些需求。默认情况下,对每次测试它们会创建并回滚事务。 你编写代码可以假定事务已经存在。如果你在测试中调用事务代理对象,它们将根据配置的事务语义正常响应。 另外,如果测试方法在事务内删除了选定表的数据,这个事务会默认回滚,数据库也将回到测试执行前的状态。 事务支持通过在测试应用上下文中定义的PlatformTransactionManager bean提供。

如果你希望事务被提交 ―― 不常见,但可能你希望特定的测试插入或修改数据库 ―― Spring集成测试支持框架 可以通过调用一个继承下来的钩子(Hook)方法或声明特定注解来让事务提交而不是回滚。

事务管理使用:

  • JUnit 3.8 遗留支持

  • TestContext框架

8.3.3.4. 集成测试支持类

Spring集成测试支持框架提供了几个abstract支持类来简化编写集成测试。 这些测试基类提供了定义良好的测试框架钩子,比如方便的变量实例和方法,来访问以下对象:

  • ApplicationContext: 用来进行显式bean查找或整体测试上下文状态。

  • JdbcTemplateSimpleJdbcTemplate: 用来查询并确认状态。 例如,你可能需要在创建对象并通过ORM工具持久化到数据库中的测试案例运行前后进行查询,以确认数据在数据库中存在了。 (Spring将确保查询在同一个事务范围内运行。) 你需要通知ORM工具来'flush'变化以确保正常工作, 例如使用Hibernate Session接口的flush()方法。

你经常会提供一个应用范围的超类来为多个集成测试提供有用的实例变量。

支持类:

  • JUnit 3.8遗留支持

  • TestContext框架

8.3.4. JDBC测试支持

org.springframework.test.jdbc包含有SimpleJdbcTestUtils类,它 是一个基于Java5的JDBC相关工具方法集,用来简化标准数据库测试场景。注意AbstractTransactionalJUnit38SpringContextTests, AbstractTransactionalJUnit4SpringContextTests, 和AbstractTransactionalTestNGSpringContextTests 提供了简便的方法来内部代理到SimpleJdbcTestUtils

8.3.5. 常用注解

Spring框架在org.springframework.test.annotation 包中提供了常用的Spring特定的注解集,如果你在Java5或以上版本开发,可以在测试中使用它。

  • @IfProfileValue

    提示一下,注解测试只针对特定的测试环境。 如果配置的ProfileValueSource类返回对应的提供者的名称, 这个测试就可以启动。这个注解可以应用到一个类或者单独的方法。

    @IfProfileValue(name="java.vendor", value="Sun Microsystems Inc.")
    public void testProcessWhichRunsOnlyOnSunJvm() {
        // some logic that should run only on Java VMs from Sun Microsystems
    }

    同时@IfProfileValue可配置一个列表 (使用OR 语义) 来在JUnit环境中获得TestNG的测试组支持。 看下面的例子:

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

    类级别注解用来指定当通过@IfProfileValue注解获取已配置的profile值时使用何种ProfileValueSource。 如果@ProfileValueSourceConfiguration没有在测试中声明,将默认使用SystemProfileValueSource

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

    在测试方法上出现这个注解时,表明底层Spring容器在该方法的执行中被“污染”,从而必须在方法执行结束后重新创建(无论该测试是否通过)。

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

    表明被注解方法预期在执行中抛出一个异常。预期异常的类型在注解中给定。如果该异常的实例在测试方法执行中被抛出, 则测试通过。同样的如果该异常实例没有在测试方法执行时抛出,则测试失败。

    @ExpectedException(SomeBusinessException.class)
    public void testProcessRainyDayScenario() {
        // some logic that should result in an Exception being thrown
    }
  • @Timed

    表明被注解的测试方法必须在规定的时间区间内执行完成(以毫秒记)。如果测试执行时间超过了规定的时间区间,测试就失败了。

    注意该时间区间包括测试方法本身的执行,任何重复测试(参见 @Repeat),还有任何测试fixture的set uptear down时间。

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

    表明被注解的测试方法必须重复执行。执行的次数在注解中声明。

    注意重复执行范围包括包括测试方法本身的执行,以及任何测试fixture的set uptear down

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

    表明被注解方法的事务在完成后是否需要被回滚。 如果true,事务将被回滚,否则事务将被提交。 使用@Rollback接口来在类级别覆写配置的默认回滚标志。

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

    出现该注解表明测试方法必须在事务中执行。

    @NotTransactional 
    public void testProcessWithoutTransaction() {
        // ...
    }

注解支持:

  • JUnit 3.8遗留支持: 所有上面列举的注解都被支持,但必须AbstractAnnotationAwareTransactionalTests类联合使用,以保证这些注解能起作用。

  • TestContext框架: 支持上面列举的所有注解,而且提供了额外的TestContext特定注解 (例如@ContextConfiguration@BeforeTransaction等等)。 注意,但是一些注解只有与JUnit联合使用时(例如,基于SpringJUnit4ClassRunner 或JUnit 3.8以及JUnit 4.4的测试类)。 详细内容参见TestContext框架章节。

8.3.6. JUnit 3.8遗留支持

Spring JUnit 3.8 遗留支持类打包在org.springframework.test包中。 这个包提供了有用的JUnit TestCase超类, 扩展它可以在容器外集成测试中引入Spring ApplicationContext类或在测试方法级别获得事务支持。

8.3.6.1. 上下文管理及缓存

AbstractSingleSpringContextTests为基于JUnit 3.8的测试案例提供了上下文管理和缓存支持。 它暴露了一个protected方法来给子类覆写以提供上下文定义文件的路径:

protected String[] getConfigLocations()

这个方法的实现必须提供包含XML配置元数据的资源路径 ―― 通常是类路径 ―― 的一个数组。 这和在web.xml或其它部署配置中的资源路径是相同的或基本相同的。 作为可选方案,你也可以覆写下面的方法。详细内容参见相关JavaDoc。

protected String[] getConfigPaths()
protected String getConfigPath()

默认情况下,一旦配置文件被载入就会在每个测试案例中重用。 这样构建的开销只会产生一次(每个测试fixture),然后后面的测试执行会快速的多。 在较少的情况下测试可能“污染”应用上下文,需要重新载入 ―― 例如, 改变一个bean定义或应用对象状态 ―― 你可以调用AbstractSingleSpringContextTests类中的 setDirty()方法来让测试fixture在执行下一个测试案例时重新载 AbstractAnnotationAwareTransactionalTests类, 你可以使用@DirtiesContext来对测试方法进行注解以达到同样的效果。

8.3.6.2. 测试fixture依赖注入

AbstractDependencyInjectionSpringContextTests类(及其子类)载入你的应用上下文时, 它们可以通过Setter注入选择性配置你的测试类实例。你需要做的仅仅是定义实例变量和相应的setter方法。 AbstractDependencyInjectionSpringContextTests将在getConfigLocations()方法定义的配置文件集中自动查找相应对象。

假定这样一个场景,我们有一个HibernateTitleDao类(在通常目标章节详述)。 让我们看基于JUnit 3.8 的测试类实现本身(我们很快将看看配置本身)。

public final class HibernateTitleDaoTests extends AbstractDependencyInjectionSpringContextTests  {

    // this instance will be (automatically) dependency injected    
    private HibernateTitleDao titleDao;

    // a setter method to enable DI of the 'titleDao' instance variable
    public void setTitleDao(HibernateTitleDao titleDao) {
        this.titleDao = titleDao;
    }

    public void testLoadTitle() throws Exception {
        Title title = this.titleDao.loadTitle(new Long(10));
        assertNotNull(title);
    }

    // specifies the Spring configuration to load for this test fixture
    protected String[] getConfigLocations() {
        return new String[] { "classpath:com/foo/daos.xml" };
    }

}

这个文件被getConfigLocations()方法指定(比如,"classpath:com/foo/daos.xml") 像这样:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <!-- this bean will be injected into the HibernateTitleDaoTests class -->
    <bean id="titleDao" class="com.foo.dao.hibernate.HibernateTitleDao">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
    
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <!-- dependencies elided for clarity -->
    </bean>

</beans>

AbstractDependencyInjectionSpringContextTests类使用按类型自动装配。 因此如果你有多个bean定义是相同的类型,就不能在这些bean中使用这种方法。 这种情况下,你可以使用继承的applicationContext实例变量并实现显式的查找(比如), 调用applicationContext.getBean("titleDao")方法。

如果你不希望在测试案例中使用依赖注入,只要不声明任何public setter方法就可以简单实现。 作为替代的,你可以扩展AbstractSpringContextTests - 在org.springframework.test 包中的JUnit 3.8集成测试支持类层次的根 - 它仅仅包含了一些载入Spring上下文的简单方法,而且不在测试fixture中使用依赖注入。

8.3.6.2.1. 字段级别(Field Level)注入

如果不管何种原因,你的测试fixture中没有setter方法,Spring可以对protected字段进行依赖注入。 下面是前面使用字段级注入示例的新版本(Spring XML文件无需改变,仅仅需要改变测试fixture)。

public final class HibernateTitleDaoTests extends AbstractDependencyInjectionSpringContextTests  {

    public HibernateTitleDaoTests() {
        // switch on field level injection
        setPopulateProtectedVariables(true);
    }

    // this instance will be (automatically) dependency injected
    protected HibernateTitleDao titleDao;

    public void testLoadTitle() throws Exception {
        Title title = this.titleDao.loadTitle(new Long(10));
        assertNotNull(title);
    }

    // specifies the Spring configuration to load for this test fixture
    protected String[] getConfigLocations() {
        return new String[] { "classpath:com/foo/daos.xml" };
    }

}

在字段注入的情况下,不能使用自动装配:protected 实例变量被作为已配置的Spring容器的bean 查找名。

8.3.6.3. 事务管理

AbstractTransactionalSpringContextTests类 依赖于应用上下文中定义的PlatformTransactionManager bean。 名字是无关紧要的,因为使用了按类型自动装配.

通常你会扩展其子类AbstractTransactionalDataSourceSpringContextTests。 这个类也需要在应用上下文中有一个DataSource bean定义(同样可以是任意名称)。 它创建一个JdbcTemplate实例变量,可以用来方便的查询和删除选定表的内容( 请记住默认情况下事务将被回滚,因而这样做是安全的)。

如果你希望编程式提交事务 ―― 不常见但对于特殊的插入数据库的测试很有用 ―― 你可以调用继承自AbstractTransactionalSpringContextTests类的setComplete() 方法。 这将使事务提交而不是回滚。作为可替代的,如果你在Java 5或更高环境中开发扩展AbstractAnnotationAwareTransactionalTests类, 你可以使用@Rollback(false)来注解测试方法,以通过配置获得相同的效果。

通过调用endTransaction()方法,这里可以在测试案例完成时中止一个事务。 默认将回滚事务,除非前面调用了setComplete()方法。 这个特性当你希望测试‘断连接’的 数据对象行为是很有用,比如事务外的web或远程使用的Hibernate映射实体。 通常懒加载错误只有通过UI测试发现。如果你调用endTransaction()方法 可以保证JUnit测试时UI操作的正确性。

8.3.6.4. JUnit 3.8 遗留支持类

当你扩展 AbstractTransactionalDataSourceSpringContextTests类时,你将需要访问下面protected 实例变量:

  • applicationContext(ConfigurableApplicationContext): 继承自AbstractSingleSpringContextTests类。使用它可以进行显式bean查找或测试整个的上下文状态。

  • jdbcTemplate: 继承自AbstractTransactionalDataSourceSpringContextTests类,用于查询已确认状态。 例如,应用代码要创建一个对象,然后使用ORM工具将其持久化,这时你想在测试代码执行前后对其进行查询,以确定数据是否插入到数据库中(Spring会保证该查询运行在相同事务内)。你需要告诉你的ORM工具‘清空’其改变以正确完成任务,例如,使用HibernateSession接口的flush()方法。

8.3.6.5. Java 5+ 专有支持

8.3.6.5.1. 使用注解的事务相关测试

在上述常用注解之外, org.springframework.test.annotation包也有一个抽象 JUnit TestCase类来提供注解驱动的集成测试支持。

AbstractAnnotationAwareTransactionalTests类扩展了AbstractTransactionalDataSourceSpringContextTests类, 并通过扩展fixture引入一些(Spring专有)的注解。AbstractAnnotationAwareTransactionalTests 支持所有常用注解章节中列举的注解, 而且包括Spring的@Transactional注解,以显式配置事务语义。

8.3.6.5.2. JPA支持类

org.springframework.test.jpa包提供了基于Java 持久化API(JPA)的测试支持类。

  • AbstractJpaTests是一个方便的JPA相关测试的支持类, 它提供了和AbstractTransactionalDataSourceSpringContextTests相同的功能和即使在进行JPA规范需要的性能测试时也相同的性能。 它暴露了一个EntityManagerFactory接口和一个共享的EntityManager接口。 需要注入一个EntityManagerFactory接口, 以及通过超类获得DataSource接口和JpaTransactionManager接口。

  • AbstractAspectjJpaTests类是AbstractJpaTests的子类, 它激活了AspectJ 的装载期织入并能够让AspectJ指定一个自定义的aop.xml文件路径。

8.3.7. Spring TestContext Framework

Spring TestContext Framework (在org.springframework.test.context包中) 提供了一般的、注解驱动的单元和集成测试支持,它对使用的测试框架不做要求,可以使用JUnit 3.8、JUnit 4.4, TestNG 5.5等等。 TestContext框架也强调了约定优于配置的重要性,它提供了合理的默认值,同时也可以通过基于注解的配置进行改写。

除了一般的测试基础设施外,TestContext框架还以抽象支持类的形式对JUnit 3.8、JUnit 4.4和TestNG 5.5提供了显式的支持。 针对JUnit 4.4,该框架还提供了一个自定义的Runner,这使得用户无需继承特定的类就可以编写测试类了。

以下章节给出了TestContext框架的内部概览。 如果你仅仅关注如何使用该框架而不是使用你自己的监听器去扩展它,那么请直接跳到配置(上下文管理和缓存、 依赖注入、事务管理)、 支持类及注解支持章节。

8.3.7.1. 主要的抽象

框架的核心包括TestContextTestContextManager类以及TestExecutionListener接口。 每次测试都会创建TestContextManagerTestContextManager管理了一个TestContext, 它负责持有当前测试的上下文。TestContextManager还负责在测试执行过程中更新TestContext的状态并代理到TestExecutionListener, 它用来监测测试实际的执行(如提供依赖注入、管理事务等等)。请查看JavaDoc及Spring测试套件以获得进一步的信息和各种配置示例。

  • TestContext:封装测试执行的上下文,与当前使用的测试框架无关。

  • TestContextManagerSpring TestContext Framework的主入口点, 负责管理单独的TestContext并在定义好的执行点上向所有注册的TestExecutionListener发出事件通知: 测试实例的准备,先于特定的测试框架的前置方法,迟于后置方法

  • TestExecutionListener:定义了一个监听器API与TestContextManager发布的测试执行事件进行交互, 而该监听器就是注册到这个TestContextManager上的。

    Spring提供了TestExecutionListener的三个实现, 他们都是使用默认值进行配置的(通过@TestExecutionListeners注解): DependencyInjectionTestExecutionListenerDirtiesContextTestExecutionListenerTransactionalTestExecutionListener, 他们对测试实例提供了依赖注入支持,处理@DirtiesContext注解,并分别使用默认的回滚语义对测试提供事务支持。

以下三个章节讲述了如何通过注解配置TestContext框架并提供了使用该框架编写真实的单元和集成测试的示例。

8.3.7.2. 上下文管理和缓存

每个TestContext都会为其所负责的测试实例提供上下文和缓存管理。 测试实例不会自动访问配置好的ApplicationContext;然而,如果一个测试类实现了ApplicationContextAware接口, 那么测试实例就会拥有一个对ApplicationContext的引用(假如默认已经配置好了DependencyInjectionTestExecutionListener)。 AbstractJUnit38SpringContextTestsAbstractJUnit4SpringContextTestsAbstractTestNGSpringContextTests已经实现了ApplicationContextAware, 因此自带了上述功能。

与JUnit 3.8遗留支持不同,使用TestContext框架的测试类无需重写任何protected成员方法来配置应用上下文。 只需在类层次上声明@ContextConfiguration注解就可以完成配置。 如果你的测试类没有显式声明任何应用上下文资源的位置,那么配置好的ContextLoader就会决定如何以及是否从默认的集合位置上加载一个上下文。 例如,GenericXmlContextLoader - 默认的ContextLoader - 会基于测试类的名字产生一个默认的位置。 如果类名叫做com.example.MyTest,那么GenericXmlContextLoader就会从"classpath:/com/example/MyTest-context.xml"加载应用上下文。

package com.example;

@RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "classpath:/com/example/MyTest-context.xml"
@ContextConfiguration
public class MyTest {
    // class body...
}

如果默认位置不适合你的需求,你可以使用一个包含了XML配置元数据的资源位置的数组来配置@ContextConfigurationlocations属性 (假如已经配置好了一个可以使用XML的ContextLoader)- 一般在classpath上,该属性被用来配置应用程序。 这就和在web.xml或者其他部署配置中指定配置列表时,方法完全一样,或者几乎一样。 作为另外一种选择,你可以实现并配置自己的ContextLoader

@RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "/applicationContext.xml" and "/applicationContext-test.xml"
// in the root of the classpath
@ContextConfiguration(locations={"/applicationContext.xml", "/applicationContext-test.xml"})
public class MyTest {
    // class body...
}

@ContextConfiguration还提供了一个boolean类型的inheritLocations属性以表明是否继承父类的locations。 其默认值是true,表明一个被注解的类会继承被注解的父类中定义的资源位置。 特别地,一个被注解的类的资源位置会附加到其被注解的父类中的资源位置列表上。这样子类就可以继承资源位置列表。 在下面的例子中,将按顺序从"/base-context.xml""/extended-context.xml"中加载针对ExtendedTestApplicationContext。 所以定义在"/extended-context.xml"中的Beans会覆盖掉定义在"/base-context.xml"中的Beans。

@RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "/base-context.xml" in the root of the classpath
@ContextConfiguration(locations={"/base-context.xml"})
public class BaseTest {
    // class body...
}

// ApplicationContext will be loaded from "/base-context.xml" and "/extended-context.xml"
// in the root of the classpath
@ContextConfiguration(locations={"/extended-context.xml"})
public class ExtendedTest extends BaseTest {
    // class body...
}

如果将inheritLocations设为false,那么就会屏蔽掉父类的资源位置,然后可以替换父类中定义的任何资源位置。

默认情况下, 配置好的ApplicationContext一旦被加载就会重用到每个测试上。这样设置的成本仅产生一次(每个测试fixture), 随后测试的执行就会很快了。在某些不太可能发生的情况下,一个测试可能会破坏应用上下文, 这时它需要重新加载 - 例如,通过改变应用对象的bean定义或者状态 - 你可以使用@DirtiesContext (假设默认已经配置了DirtiesContextTestExecutionListener)来注解测试方法使得测试fixture重新加载配置文件并在测试下次执行前重新构建应用上下文。

8.3.7.3. 测试fixture的依赖注入

当你配置DependencyInjectionTestExecutionListener时 - 它会被默认配置 - 通过@TestExecutionListeners注解, 你的测试实例依赖的bean会被注入,而这些bean是通过@ContextConfiguration使用Setter注入、Field注入或者两者都有来注入的, 到底使用哪种方式取决于你选择的注解以及你将它们放到setter方法中还是属性中。为了与Spring 2.5的注解保持一致, 你可以选择Spring的@Autowired注解或者JSR 250中的@Resource注解。其语义对于Spring框架来说都是一致的。 例如, 如果你喜欢 按类型自动织入, 那么请使用@Autowired来注解你的settter方法或者属性。另一方面,如果你喜欢按名字注入, 那么请使用@Resource来注解你的settter方法或者属性。

提示

TestContext框架没有监测测试实例的实例化方式。所以对构造方法使用@Autowired将毫无意义。

既然@Autowired执行按类型自动编织, 那么如果你有相同类型的多个bean定义的话,对那些特定的bean就不能使用该方式。在这种情况下, 你可以使用@Resource按名字注入。另外,如果你的测试类实现了ApplicationContextAware, 就可以直接访问ApplicationContext并调用applicationContext.getBean("titleDao")执行一个显式的查找。

如果你不想让你的测试实例使用依赖注入,只要不将@Autowired或者@Resource注解到任何属性或者setter方法上就行了。 另一种方式,你可以使用@TestExecutionListeners并忽略掉监听器列表中的DependencyInjectionTestExecutionListener.class就可以完全禁用依赖注入。

考虑如下场景:我们有一个类,名字叫HibernateTitleDao(通用目标章节已经进行了介绍)。 首先,让我们看看基于JUnit 4.4的测试类的实现,它使用@Autowired进行属性注入(在所有示例代码之后我们会查看应用上下文的配置)。 注意:下面代码的依赖注入行为并不是特定于JUnit 4.4的。同样的依赖注入技术可以使用在任何测试框架中。

@RunWith(SpringJUnit4ClassRunner.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration(locations={"daos.xml"})
public final class HibernateTitleDaoTests {

    // this instance will be dependency injected by type
    @Autowired    
    private HibernateTitleDao titleDao;

    public void testLoadTitle() throws Exception {
        Title title = this.titleDao.loadTitle(new Long(10));
        assertNotNull(title);
    }
}

此外,我们可以使用@Autowired进行setter注入。

@RunWith(SpringJUnit4ClassRunner.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration(locations={"daos.xml"})
public final class HibernateTitleDaoTests {

    // this instance will be dependency injected by type
    private HibernateTitleDao titleDao;

    @Autowired
    public void setTitleDao(HibernateTitleDao titleDao) {
        this.titleDao = titleDao;
    }

    public void testLoadTitle() throws Exception {
        Title title = this.titleDao.loadTitle(new Long(10));
        assertNotNull(title);
    }
}

现在让我们看看使用@Resource进行属性注入的一个示例。

@RunWith(SpringJUnit4ClassRunner.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration(locations={"daos.xml"})
public final class HibernateTitleDaoTests {

    // this instance will be dependency injected by name
    @Resource
    private HibernateTitleDao titleDao;

    public void testLoadTitle() throws Exception {
        Title title = this.titleDao.loadTitle(new Long(10));
        assertNotNull(title);
    }
}

最后,这是使用@Resource进行setter注入的一个示例。

@RunWith(SpringJUnit4ClassRunner.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration(locations={"daos.xml"})
public final class HibernateTitleDaoTests {

    // this instance will be dependency injected by name
    private HibernateTitleDao titleDao;
    
    @Resource
    public void setTitleDao(HibernateTitleDao titleDao) {
        this.titleDao = titleDao;
    }

    public void testLoadTitle() throws Exception {
        Title title = this.titleDao.loadTitle(new Long(10));
        assertNotNull(title);
    }
}

上面的代码使用了相同的XML上下文文件,@ContextConfiguration注解使用了这些信息(如 "daos.xml"),它是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <!-- this bean will be injected into the HibernateTitleDaoTests class -->
    <bean id="titleDao" class="com.foo.dao.hibernate.HibernateTitleDao">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
    
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <!-- dependencies elided for clarity -->
    </bean>

</beans>

8.3.7.4. 事务管理

在TestContext框架中,事务是由TransactionalTestExecutionListener进行管理的, 默认情况下这是通过@TestExecutionListeners注解进行配置的, 即使你没有在测试类中显式声明 @TestExecutionListeners注解。 为了支持事务,你必须通过@ContextConfiguration在应用上下文中加载一个PlatformTransactionManager bean。 此外,你必须在类或方法层次上声明一个@Transactional

请参考TestContext框架的注解支持中的@TransactionConfiguration以了解类层次的事务配置(例如为事务管理器设置bean名称以及默认的回滚标志)。

为每个测试方法配置事务时有几种选项。如果对于整个类来说事务不可用,那么可以使用@Transactional来显式注解方法。 与此类似,如果对于整个类来说事务可用,那么可以使用@NotTransactional来注解方法表明不为该方法增加事务。 你可以使用@Rollback注解覆盖类级别的默认的回滚设置进而针对一个特定的测试方法控制其事务的提交。

请注意,AbstractTransactionalJUnit38SpringContextTestsAbstractTransactionalJUnit4SpringContextTestsAbstractTransactionalTestNGSpringContextTests已经在类级别预先配置好了事务支持。

偶尔你需要在一个事务性测试方法前、后执行某些代码,而这些代码是处在事务上下文之外的,例如, 在测试执行前去验证初始的数据库状态或者在测试执行后验证期待的事务提交行为(举例来说,该测试被配置为不进行回滚的)。 支持@BeforeTransaction@AfterTransaction注解的TransactionalTestExecutionListener正好适用于这种情况。 使用这些注解之一来注解测试类中任何的public void方法, 同时TransactionalTestExecutionListener会保证你的事务方法之前的代码或者事务方法之后的代码会在正确的时间执行。

提示

任意前置方法 (如使用JUnit 4的@Before所注解的方法)和后置方法 (如使用JUnit 4的@After所注解的方法)都会一个事务中得到执行。 此外,使用 @NotTransactional注解的测试不会执行@BeforeTransaction@AfterTransaction所注解的方法。

下面的基于JUnit 4的示例展示了一个假想的集成测试场景,重点阐述了事务相关的注解。请查看参考手册的TestContext框架注解支持章节以了解进一步的信息和配置示例。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
@Transactional
public class FictitiousTransactionalTest {

	@BeforeTransaction
	public 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 defaultRollback setting
	@Rollback(true)
	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
	public void verifyFinalDatabaseState() {
		// logic to verify the final state after transaction has rolled back
	}

	@Test
	@NotTransactional
	public void performNonDatabaseRelatedAction() {
		// logic which does not modify database state
	}
}

8.3.7.5. TestContext支持类

8.3.7.5.1. JUnit 3.8支持类

org.springframework.test.context.junit38包为基于JUnit 3.8的测试用例提供了支持类。

  • AbstractJUnit38SpringContextTests

    对集成了Spring TestContext Framework与JUnit 3.8环境中的ApplicationContext测试支持的TestCase进行了抽象。 当你继承AbstractJUnit38SpringContextTests类时,你就可以访问到protected的成员变量:

    • applicationContext:使用它进行显式的bean查找或者测试整个上下文的状态。

  • AbstractTransactionalJUnit38SpringContextTests

    对为JDBC访问增加便捷功能的AbstractJUnit38SpringContextTests事务扩展进行抽象。 需要在ApplicationContext中定义一个javax.sql.DataSource bean和一个PlatformTransactionManager bean。 当你继承AbstractTransactionalJUnit38SpringContextTests类时,你就可以访问到protected的成员变量:

    • applicationContext:从AbstractJUnit38SpringContextTests父类继承。使用它执行bean的查找或者测试整个上下文的状态

    • simpleJdbcTemplate:在查询以确认状态时非常有用。例如,应用代码要创建一个对象,然后使用ORM工具将其持久化, 这时你想在测试代码执行前后对其进行查询,以确定数据是否插入到数据库中(Spring会保证该查询运行在相同事务内)。 你需要告诉你的ORM工具‘flush’其改变以正确完成任务,例如,使用HibernateSession接口的flush()方法。

8.3.7.5.2. JUnit 4.4支持类

org.springframework.test.context.junit4包为基于JUnit 4.4的测试用例提供了支持类。

  • AbstractJUnit4SpringContextTests

    对集成了Spring TestContext Framework与JUnit 4.4环境中的ApplicationContext测试支持的基本测试类进行了抽取。

    当你继承AbstractJUnit4SpringContextTests时,你就可以访问到protected的成员变量:

    • applicationContext:使用它进行显式的bean查找或者测试整个上下文的状态。

  • AbstractTransactionalJUnit4SpringContextTests

    对为JDBC访问增加便捷功能的AbstractJUnit4SpringContextTests事务扩展进行抽象。 需要在ApplicationContext中定义一个javax.sql.DataSource bean和一个PlatformTransactionManager bean。

    当你继承AbstractTransactionalJUnit4SpringContextTests类时,你就可以访问到下列protected的成员变量:

    • applicationContext:继承自父类AbstractJUnit4SpringContextTests。 使用它执行bean的查找或者测试整个上下文的状态

    • simpleJdbcTemplate:当通过查询来确认状态时非常有用。例如,应用代码要创建一个对象, 然后使用ORM工具将其持久化,这时你想在测试代码执行前后对其进行查询,以确定数据是否插入到数据库中。 (Spring会保证该查询运行在相同事务内。)你需要告诉你的ORM工具‘flush’其改变以正确完成任务,例如, 使用HibernateSession接口的flush()方法。

提示

这些类仅仅为扩展提供了方便。 如果你不想将你的测试类绑定到Spring的类上 - 例如,如果你要直接扩展你想测试的类 - 只需要通过@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration@TestExecutionListeners等注解来配置你自己的测试类就可以了。

8.3.7.5.3. 定制JUnit 4.4运行器

Spring TestContext Framework通过一个可定制的运行器提供了与JUnit 4.4的完全集成。 通过使用@Runwith(SpringJUnit4ClassRunner.class)来注解测试类,开发者可以实现标准的JUnit 4.4单元和集成测试, 同时还能获得TestContext框架的好处,如对加载应用上下文的支持,测试实例的依赖注入,执行事务性测试方法等等。 下面的代码清单显示了使用定制的Spring Runner来配置一个测试类的最小需求。 注意,我们使用一个空的列表来配置@TestExecutionListeners以便禁用默认的监听器, 否则需要通过@ContextConfiguration配置一个 ApplicationContext

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({})
public class SimpleTest {

	@Test
	public void testMethod() {
		// execute test logic...
	}
}
8.3.7.5.4. TestNG支持类

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

  • AbstractTestNGSpringContextTests

    对集成了Spring TestContext Framework与TestNG环境中的ApplicationContext测试支持的基础测试类进行了抽象。

    当你继承AbstractTestNGSpringContextTests时,就可以访问到下列protected的成员变量:

    • applicationContext:使用它进行显式的bean查找或者测试整个上下文的状态。

  • AbstractTransactionalTestNGSpringContextTests

    对为JDBC访问增加便捷功能的AbstractTestNGSpringContextTests事务扩展进行抽象。 需要在ApplicationContext中定义一个javax.sql.DataSource bean和一个PlatformTransactionManager bean。

    当你继承AbstractTransactionalTestNGSpringContextTests类时,就可以访问下列protected的成员变量:

    • applicationContext:继承自父类AbstractTestNGSpringContextTests。使用它执行bean的查找或者测试整个上下文的状态。

    • simpleJdbcTemplate:当通过查询来确认状态时非常有用。例如,应用代码要创建一个对象, 然后使用ORM工具将其持久化,这时你想在测试代码执行前后对其进行查询,以确定数据是否插入到数据库中。(Spring会保证该查询运行在相同事务内。) 你需要告诉你的ORM工具‘flush’其改变以正确完成任务,例如,使用HibernateSession接口的flush()方法。

提示

这些类仅仅为扩展提供了方便。 如果你不想将你的测试类绑定到Spring的类上 - 例如,如果你要直接扩展你想测试的类 - 只需要通过 @ContextConfiguration@TestExecutionListeners等注解来配置你自己的测试类就可以了。 并使用TestContextManager来手工监测你的测试类。 请查看AbstractTestNGSpringContextTests的源代码以了解如何检测你自己的测试类。

8.3.7.6. TestContext框架注解支持

Spring TestContext Framework支持通用注解章节提到的所有注解。 然而下面的这些注解只有配合JUnit才能使用(比如搭配SpringJUnit4ClassRunner或者 JUnit 3.8及JUnit 4.4支持类)。

  • @IfProfileValue

  • @ProfileValueSourceConfiguration

  • @ExpectedException

    协同使用Spring的@ExpectedException注解与JUnit 4的@Test(expected=...)会导致一个不可避免的冲突。 因此当与JUnit 4集成时,开发者必须选择其中一个,在这种情况下建议使用显式的JUnit 4配置。

  • @Timed

    Spring的@Timed注解与JUnit 4的@Test(timeout=...)支持具有不同的语义。 特别地,鉴于JUnit 4处理测试执行超时(如通过在一个单独的线程中执行测试方法)的方式, 我们不可能在一个事务上下文中的测试方法上使用JUnit的@Test(timeout=...)配置。因此, 如果你想将一个测试方法配置成计时具事务性的, 你就必须联合使用Spring的@Timed@Transactional注解。 还值得注意的是@Test(timeout=...)只管测试方法本身执行的次数,如果超出的话立刻就会失败; 然而,@Timed关注的是测试执行的总时间(包括建立和销毁操作以及重复),并且不会令测试失败。

  • @Repeat

Spring TestContext Framework还支持下面这些非特定于测试的注解,并且保持其语义不变。

  • @Autowired

  • @Qualifier

  • @Resource (javax.annotation)如果JSR-250可用

  • @PersistenceContext (javax.persistence)如果JPA可用

  • @PersistenceUnit (javax.persistence)如果JPA可用

  • @Required

  • @Transactional

下面的列表包含了特定于Spring TestContext Framework的所有注解。请查看相应的JavaDoc以了解进一步的信息,包括默认的属性值等等。

  • @ContextConfiguration

    定义类级别的元数据以决定如何加载和配置ApplicationContext。特别地, @ContextConfiguration定义了要加载的应用上下文资源位置以及用来加载上下文的ContextLoader策略。

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

    注意:@ContextConfiguration默认情况下为继承的资源位置提供了支持。 查看上下文管理和缓存章节及JavaDoc来了解更多的示例和细节信息。

  • @TestExecutionListeners

    定义类级别的元数据,TestExecutionListeners会使用TestContextManager进行注册。 通常,@TestExecutionListeners@ContextConfiguration会搭配使用。

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

    注意:@TestExecutionListeners默认情况下为继承的监听器提供了支持。查看JavaDoc来了解更多的示例和细节信息。

  • @TransactionConfiguration

    为配置事务性测试定义了类级别的元数据。特别地,如果需要的PlatformTransactionManager不是“transactionManager”的话, 那么可以显式配置驱动事务的PlatformTransactionManager的bean名字。此外, 可以将defaultRollback标志改为false。通常, @TransactionConfiguration@ContextConfiguration搭配使用。

    @ContextConfiguration
    @TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
    public class CustomConfiguredTransactionalTests {
        // class body...
    }
  • @BeforeTransaction

    表明被注解的public void方法应该在测试方法的事务开始之前执行, 该事务是通过@Transactional注解来配置的。

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

    表明被注解的public void方法应该在测试方法的事务结束之后执行, 该事务是通过@Transactional注解来配置的。

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

8.3.8. PetClinic示例

在Spring的完整发行包里包含了PetClinic示例应用,它以JUnit 4.4环境阐述了Spring TestContext Framework的几个特性。 大多数功能包含在AbstractClinicTests里,部分内容列举如下:

@ContextConfiguration
public abstract class AbstractClinicTests extends AbstractTransactionalJUnit4SpringContextTests {

	@Autowired
	protected Clinic clinic;

	@Test
	public void getVets() {
		Collection<Vet> 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());
		// ...
	}
	
	// ...
}

注意:

  • 该测试用例继承了AbstractTransactionalJUnit4SpringContextTests类, 从这里它继承了针对依赖注入的配置(通过DependencyInjectionTestExecutionListener)和事务性行为(通过TransactionalTestExecutionListener)。

  • clinic成员变量 - 要测试的应用程序对象 - 是通过@Autowired进行依赖注入的。

  • testGetVets()方法说明如何使用继承下来的countRowsInTable()方法来轻松验证表中的行数, 进而测试应用代码的正确行为。这点允许实现更强大的测试,减少了对确切测试数据的依赖。例如,无需打断测试就可以向数据库中增加新行。

  • 像很多使用数据库的集成测试一样,AbstractClinicTests中的大多数测试依赖于测试运行前数据库中已有的最小量的数据。 但是你可能在测试用例中改变数据库――当然,在同一个事务中。

PetClinic应用支持三种数据访问技术 - JDBC、Hibernate及JPA。无需任何特定的资源位置, 只要声明了@ContextConfiguration,那么AbstractClinicTests类就会从默认位置加载其应用上下文, 该默认位置为"AbstractClinicTests-context.xml",这里声明了一个通用的DataSource。 子类指定了额外的上下文位置,这就要求它必须声明一个PlatformTransactionManagerClinic的一个具体实现。

例如,PetClinic测试的Hibernate实现包含以下实现。针对这个例子请注意,HibernateClinicTests没有包含一行代码: 我们只需声明@ContextConfiguration并且测试继承于AbstractClinicTests。 既然无需任何特定的资源位置就可以声明@ContextConfiguration, 那么Spring TestContext Framework就会从"AbstractClinicTests-context.xml" (例如继承的位置)和 "HibernateClinicTests-context.xml"中加载应用上下文, 同时"HibernateClinicTests-context.xml" 中定义的bean会覆盖掉"AbstractClinicTests-context.xml"中定义的bean。

@ContextConfiguration
public class HibernateClinicTests extends AbstractClinicTests { }

正如你在PetClinic应用中所看到的,Spring配置文件被划分成多个文件。对于大型应用来说都是这样做的, 配置位置通常被指定在一个针对该应用程序集成测试的通用基类中。 这样的基类还可以增加有用的实例变量 - 很自然地由依赖注入组装 - 例如使用Hibernate的应用中的HibernateTemplate

从长远来看,集成测试中的Spring配置文件应该与部署环境中的一样。一个可能的不同点是数据库连接池和事务基础设施。 如果你正部署到一个完整的应用服务器上,那你可能会使用其连接池(通过JNDI访问)和JTA实现。 这样依赖,在生产阶段你会使用JndiObjectFactoryBean来获得DataSourceJtaTransactionManager。 在容器外的集成测试中无法使用JNDI和JTA,因此你应该为他们使用一个替代的组合, 如Commons DBCP BasicDataSourceDataSourceTransactionManager或者HibernateTransactionManager。 你可以将这种不同的行为放到一个单独的XML文件中,在应用服务器和独立于其他配置的'本地'配置中自由选择,这不会在测试和产品环境中造成差异。 此外,建议使用属性文件来存放连接信息:请查看PetClinic应用以了解这些。

上一篇: 下一篇: