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

使用 Spring 2.5 TestContext 测试框架

2010-07-25 22:17 537 查看
Spring 2.5 TestContext 测试框架用于测试基于 Spring 的程序,TestContext 测试框架和低版本 Spring 测试框架没有任何关系,是一个全新的基于注解的测试框架,为 Spring 推荐使用该测试框架。

概述

Spring 2.5 相比于 Spring 2.0 所新增的最重要的功能可以归结为以下 3 点:

基于注解的 IoC 功能;

基于注解驱动的 Spring MVC 功能;

基于注解的 TestContext 测试框架。

Spring 推荐开发者使用新的基于注解的 TestContext 测试框架,本文我们将对此进行详细的讲述。
低版本的 Spring 所提供的 Spring 测试框架构在 JUnit 3.8 基础上扩展而来,它提供了若干个测试基类。而 Spring 2.5 所新增的基于注解的 TestContext 测试框架和低版本的测试框架没有任何关系。它采用全新的注解技术可以让 POJO 成为 Spring 的测试用例,除了拥有旧测试框架所有功能外,TestContext 还添加了一些新的功能,TestContext 可以运行在 JUnit 3.8、JUnit 4.4、TestNG 等测试框架下。

直接使用 JUnit 测试 Spring 程序存在的不足

在拙作《精通 Spring 2.x — 企业应用开发详解》一书中,笔者曾经指出如果直接使用 JUnit 测试基于 Spring 的程序,将存在以下 4 点明显的不足:

导致 Spring 容器多次初始化问题:根据 JUnit 测试用例的调用流程,每执行一个测试方法都会重新创建一个测试用例实例并调用其 setUp() 方法。由于在一般情况下,我们都在 setUp() 方法中初始化 Spring 容器,这意味着测试用例中有多少个测试方法,Spring 容器就会被重复初始化多少次。

需要使用硬编码方式手工获取 Bean:在测试用例中,我们需要通过 ApplicationContext.getBean() 的方法从 Spirng 容器中获取需要测试的目标 Bean,并且还要进行造型操作,即如下:

//加载spring配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//用getBean()方法获取spring配置文件里定义过的bean,这里不能用注解的方式获得
customerService = (CustomerService)applicationContext.getBean("customerService");


数据库现场容易遭受破坏:测试方法可能会对数据库记录进行更改操作,破坏数据库现场。虽然是针对开发数据库进行测试工作的,但如果数据操作的影响是持久的,将会形成积累效应并影响到测试用例的再次执行。举个例子,假设在某个测试方法中往数据库插入一条 ID 为 1 的 t_user 记录,第一次运行不会有问题,第二次运行时,就会因为主键冲突而导致测试用例执行失败。所以测试用例应该既能够完成测试固件业务功能正确性的检查,又能够容易地在测试完成后恢复现场,做到踏雪无迹、雁过无痕。

不容易在同一事务下访问数据库以检验业务操作的正确性:当测试固件操作数据库时,为了检测数据操作的正确性,需要通过一种方便途径在测试方法相同的事务环境下访问数据库,以检查测试固件数据操作的执行效果。如果直接使用 JUnit 进行测试,我们很难完成这项操作。

Spring 测试框架是专门为测试基于 Spring 框架应用程序而设计的,它能够让测试用例非常方便地和 Spring 框架结合起来,以上所有问题都将迎刃而解。

这里需要增加了spring-test-2.5.6.jar和junit-4.4.jar两个用于测试的包!
这里尤其要说明一下,由于我们使用注解方式自然要用到JUnit-4.X系列,而Sring-Test对于JUnit有个累人的要求,JUnit的版本必须是4.4,不支持高版本(如4.5、4.7等)。否则,会产生java.lang.ClassNotFoundException: org.junit.Assume$AssumptionViolatedException异常。所以当没有JUnit4.4或其它相关的测试环境时,就导入junit-4.4.jar包

实例:
1 实体类User.java
package org.lab24.entity;

import java.io.Serializable;

public class User implements Serializable{

public User(){
}

private Integer id;
private String name;

public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}


对应的hibernate配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.lab24.entity">
<class name="User" table="user"><!-- 因为怕group为关键字,所以改一下 -->

<id name="id">
<generator class="native"/>
</id>

<property name="name" type="string" length="50"/>

</class>
</hibernate-mapping>


2 接口类:
package org.lab24.service;

import java.util.List;

import org.lab24.entity.User;

public interface UserService {

public void save(User user);

public List<User> getAllUsers();

}


3 实现类:
package org.lab24.serviceImpl;

import java.util.List;

import javax.annotation.Resource;

import org.hibernate.SessionFactory;
import org.lab24.entity.User;
import org.lab24.service.UserService;
import org.springframework.transaction.annotation.Transactional;

//数据库操作一般要开启事业,不然抛异常
@Transactional
public class UserServiceImpl implements UserService{

@Resource
SessionFactory sessionFactory;

public List<User> getAllUsers() {

return  sessionFactory.getCurrentSession().createQuery("from User").list();
}

public void save(User user) {
sessionFactory.getCurrentSession().persist(user);
}

}


4 测试类:
package org.lab24.junit.test;

import java.util.Iterator;
import java.util.List;

import javax.annotation.Resource;

import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.lab24.entity.User;
import org.lab24.service.UserService;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)

//@Transactional
/*
* 这里在注意的是,在spring配置文件里配置事务管理器(org.springframework.orm.hibernate3.HibernateTransactionManager)时,
* 当用的bean名字不是transactionManager,如配置成:<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">时,
* 那么就要在这里再加上@TransactionConfiguration(transactionManager = "txManager"),不然抛出异常,找不到默认transactionManager
*/
//@TransactionConfiguration(transactionManager = "txManager", defaultRollback = true)

public class UserServiceImplTest extends AbstractTransactionalJUnit4SpringContextTests{

@Resource
private UserService userService;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
//用spring2.5测试框架就不用下面的方式来初始化spring容器
//		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
}

@Test
public void testGetAllUsers() {
List<User> users = userService.getAllUsers();
Iterator iterator = users.iterator();
User temp;
while(iterator.hasNext()){
temp = (User) iterator.next();
System.out.println("ID:" + temp.getId() + "   name:" + temp.getName());
}
}

@Test
public void testSave() {
User user = new User();

user.setName("binge");
userService.save(user);
}

}

5 spring配置文件applicationContext.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:annotation-config/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.gjt.mm.mysql.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/www?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value=""/>
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="1"/>
<!-- 连接池的最大值 -->
<property name="maxActive" value="500"/>
<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
<property name="maxIdle" value="2"/>
<!--  最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
<property name="minIdle" value="1"/>
</bean>

<!-- 下面是用hiberanate的二级缓存的配置,先生成sessionFactory,再将数据源(上面配好的),注入到这里的name=dataSource这里来-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 下面完成hiberate的配置映射文件,它可以有多条的映射文件列表 -->
<property name="mappingResources">
<list>
<value>org/lab24/entity/User.hbm.xml</value>
</list>
</property>
<!-- 下面是对hibernate的属性的设置 -->
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.hbm2ddl.auto=update
hibernate.show_sql=true<!-- 测试时用true来看hibernate的操作过程,当发时,要改为false -->
hibernate.format_sql=true
hibernate.cache.use_second_level_cache=true<!-- 使用二级缓存 -->
hibernate.cache.use_query_cache=false
hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
</value>
</property>
</bean>

<!-- 下面是配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/><!-- 将上面的(ref="sessionFactory")交由事务管理器(name="sessionFactory")来管理 -->
</bean>

<!-- 下面是打开注解的支持,并设置了事务管理器 (上面配置的事务管理器)-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 下面将bean交给spring容器管理,就可以使用依赖注入的方式来引用bean -->
<bean id="userService" class="org.lab24.serviceImpl.UserServiceImpl"/>

</beans>


6 这里个使用到了ecache,它的配置文件ehcache.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!--
defaultCache节点为缺省的缓存策略
maxElementsInMemory 内存中最大允许存在的对象数量
eternal 设置缓存中的对象是否永远不过期
overflowToDisk 把溢出的对象存放到硬盘上
timeToIdleSeconds 指定缓存对象空闲多长时间就过期,过期的对象会被清除掉
timeToLiveSeconds 指定缓存对象总的存活时间
diskPersistent 当jvm结束是是否持久化对象
diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
-->
<ehcache>
<diskStore path="D:/cache"/>
<defaultCache  maxElementsInMemory="1000" eternal="false" overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="180"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="60"/>
<cache name="cn.itcast.bean.Person" maxElementsInMemory="100" eternal="false"
overflowToDisk="true" timeToIdleSeconds="300" timeToLiveSeconds="600" diskPersistent="false"/>
</ehcache>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: