使用Spring2.5 TestContext框架
一、使用Spring2.5 TestContext框架
参阅:http://www.ibm.com/developerworks/cn/java/j-lo-spring25-test/
Spring 2.5 TestContext 测试框架,是一个全新的基于注解的测试框架,为 Spring 推荐使用该测试框架。
Spring 2.5 相比于 Spring 2.0 所新增的最重要的功能可以归结为以下 3 点:
(1)基于注解的 IoC 功能;
(2)基于注解驱动的 Spring MVC 功能;
(3)基于注解的 TestContext 测试框架。
Spring 推荐开发者使用新的基于注解的 TestContext 测试框架。
1、一个需要测试的 Spring 服务类(测试需求)
在用户登录成功后调用 UserService 中的 handleUserLogin() 方法执行用户登录成功后的业务逻辑:
(1)登录用户添加 5 个积分(t_user.credits);
(2)登录用户的最后访问时间(t_user.last_visit)和 IP(t_user.last_ip)更新为当前值;
(3)在日志表中(t_login_log)中为用户添加一条登录日志。
这是一个需要访问数据库并存在数据更改操作的业务方法,它工作在事务环境下。
2、编写 UserService 的测试用例
(1)TestUserService继承AbstractTransactionalJUnit4SpringContextTests。
(2)标注一个类级的 @ContextConfiguration 注解。TestUserService-context.xml 的 Spring 默认配置文件。@ContextConfiguration两个常用的属性:locations:inheritLocations。
(3)属性 @Autowired 注解。让 Spring 容器自动注入 UserService 类型的 Bean。
(4)测试方法@Test 注解。@Test 是 JUnit 4.4 所定义的注解。
TestUserService-context.xml 配置文件的内容:
<import resource="classpath:/applicationContext.xml"/>
3、准备测试数据并检测运行结果
(1)准备测试数据
使用 JUnit 4.4 的 @Before 注解达到这个目的。
在 Spring TestContext 中,标注 @Before 和 @After 的方法会在测试用例中每个测试方法运行前后执行,并和测试方法运行于同一个事务中。由于测试方法运行后,整个事务会被回滚,在@Before中插入的测试数据也不会持久化到数据库中,因此我们无须手工删除这条记录。
要不在同一个事务中,保存准备数据。使用 Spring TestContext 的 @BeforeTransaction 和 @AfterTransaction 注解来达到目的。
可以显式地通过 @NotTransactional 注解,让测试方法不工作于事务环境下。
准备测试数据时,常使用的对象(在 AbstractTransactionalJUnit4SpringContextTests 抽象类中定义):只要 Spring 容器有配置数据源,simpleJdbcTemplate 就会被自动创建。同时该抽象类中还拥有一个 Spring 容器引用:applicationContext,你可以借助该成员变量访问 Spring 容器,执行获取 Bean,发布事件等操作。
此外,AbstractTransactionalJUnit4SpringContextTests 还提供了若干个访问数据库的便捷方法,说明如下:
protected int countRowsInTable(String tableName) :计算数据表的记录数。
protected int deleteFromTables(String... names):删除表中的记录,可以指定多张表。
protected void executeSqlScript(String sqlResourcePath, boolean continueOnError):执行 SQL 脚本文件,在脚本文件中,其格式必须一个 SQL 语句一行。
(2)检验业务逻辑的正确性
没有在业务方法执行后检查执行结果的正确性,测试是不到位的。也就是说,我们必须访问数据库以检查业务方法对数据更改是否成功:这包括积分(credits)、最后登录时间(last_visit)、最后登录 IP(last_ip)以及登录日志表中的登录日志记录(t_login_log)。
注意:由于我们的 DAO 层采用 Spring JDBC 框架,它没有采用服务层缓存技术,所以可以使用 DAO 类返回数据库中的数据。如果采用 Hibernate 等 ORM 框架,由于它们采用了服务层缓存的技术,为了获取数据库中的相应数据,需要在业务方法执行后调用 HibernateTemplate.flush() 方法,将缓存中的对象同步到数据库中,这时才可以通过 SimpleJdbcTemplate 在数据库中访问业务方法的执行情况。
二、利用spring2.0来进行集成测试
参阅:http://wiki.springside.org.cn/display/springside/SpringUnitTest
http://www.redsaga.com/spring_ref/2.0RC2/html/testing.html
Spring下单元测试的要点
1. 需要Spring 依赖注入的测试
为了测试Spring管理下的Bean,可以自行构造BeanFactory,也可以继承于AbstractDependencyInjectionSpringContextTests,实现public String[] getConfigLocations()函数, 返回applicationContext文件路径的数组。
protected String[] getConfigLocations() {
return new String[]{"classpath*:spring/*.xml", "classpath*:spring/test/*.xml"};
}
并显式写一些需要注入的变量的setter函数。
tips1:此基类有一个applicationContext的成员变量,所以除了依靠setter注入外,还可以随时用applicationContext.getBean(String beanName) 取出所需的bean。
2. Dao测试
AbstractTransactionalDataSourceSpringContextTests继承于AbstractDependencyInjectionSpringContextTests,除了拥有上类的能力外,还管理了每个测试的事务,会Open Session In Test,还会在每个测试后默认回滚所有的操作。
tips1:如果需要在测试后提交,需要setRollBack(false); 或者调用setComplete(); 注意如果没有提交,hibernate这样奸诈的Framework就不会去实际操作数据库,降低了测试的效果。
3. Controller测试
Controller测试一般要用MockObject 分离Service层,要copy WEB-INF/下的相关文件copy 到classpath,而且Controller不含太多的逻辑,所有测试controller有点吃力不讨好,建议直接用Selenium进行集成测试,见(Selenium测试概述)。
相关API:
1、AbstractSpringContextTests类[1],该类全部方法是protected的,主要用于子类重写。
2、AbstractDependencyInjectionSpringContextTests类[2]:继承于类[1]:名字N长的。如果仅仅使用Spring依赖注入功能,可以让测试用例继承该类。
3、AbstractTransactionalSpringContextTests类[3]:继承于类[2],继承该类的测试用例在spring管理的事务中进行,测试完后对数据库的记录不会造成任何影响。
4、AbstractTransactionalDataSourceSpringContextTests:继承于类[3],功能更强大,用于测试持久层组件,看其源代码,有一行"protected JdbcTemplate jdbcTemplate;",提供了一个JdbcTemplate的变量,通过该对象可以直接操作数据库。
三、利用spring的mock类进行单元测试
spring框架提供了大量测试的mock类,包括与jndi,porlet,web应用相关的mock类。尤其是web应用相关的mock类,可以大大提高web组件测试的方便性。
打开spring的下载包的mock文件夹(路径...mock\org\springframework\mock\web),就发觉有如下几个文件:
MockHttpServletRequest:是HttpServletRequest接口的mock实现,用来模拟客户端的HTTP请求,很常用的一个类。
MockHttpServletResponse:是HttpServletResponse接口的mock实现,用于模拟服务器对客户端的响应。
MockHttpSession:是对HttpSession接口的mock实现。
DelegatingServletInputStream:是对ServletInputStream接口的mock实现。
DelegatingServletOutputStream:ServletOutputStream的mock实现。需要拦截和分析服务器的输出的流的内容,可以使用该类。
其他的,例如MockFilterConfig,MockPageContext(可以测试预编译的 JSP),MockRequestDispatcher,MockServletConfig看名称就知道大概是mock什么的。
举一个例子: Java代码
MockHttpServletRequest request = new MockHttpServletRequest("POST","/index.do");
request.addParameter("username","name");
request.addParameter("password","word");