Junit 是用于自动化测试Java代码的框架,而Unitils是测试整合之王,目的是 让单元测试更加容易和可维护,Unitils构建在DbUnit与EasyMock 项目之上并与JUnit和TestNG相结合,支持数据库测试,支持利用Mock对象进行测试并提供与Spring和Hibernate相集成。Unitils模块组件有一下组成:
1、unitils-core: 核心内核包
2、unitils-database: 维护测试数据库及连接池
3、unitils-DbUnit: 使用Dbunit管理测试数据
4、unitils-easymock:支持创建Mock 和宽松的反射参数匹配
5、unitils-inject: 支持一个对象注入另一个对象
6、unitils-mock:整合各种Mock,在Mock 的使用语法上进行简化。
7、unitils-orm:支持Hibernate、JPA 的配置和自动数据库映射检查
8、unitils-spring:支持加载spring 的上下文配置,并检索和Spring Bean 注入。
Unitils 配置文件:
1、unitils-default.properites; 默认配置属性
2、unitils.properties:项目级配置属性
3、unitils-local.properties:特定用户的配置属性
Unitils断言:
1、assertReflectionEquals: 反射断言
<span style="white-space:pre"> </span>@Test
public void testReflectionEquals(){
User user1 = new User("tom","1234");
User user2 = new User("tom","1234");
assertReflectionEquals(user1, user2);//循环比较对象属性值是否相等
}
2、assertLenientEquals: 忽略顺序和时间设置的断言
<span style="white-space:pre"> </span>@Test
public void testArrayLenientEquals(){
Integer orderList1[] = new Integer[]{1,2,3};
Integer orderList2[] = new Integer[]{3,2,1};
assertLenientEquals(orderList1, orderList2);
}
3、assertPropertyXxxEquals: 属性断言
@Test
public void testPropertyLenientEquals(){
User user = new User("tom","1234");
assertPropertyReflectionEquals("userName", "tom", user);
assertPropertyLenientEquals("lastVisit", null, user);
}
集成Spring:
支持一下特性:
1、ApplicationContext 配置管理
2、在单元测试代码中注入Spring的Beans;
3、使用定义在Spring配置文件中的Hibernate SessionFacotry
4、引用在Spring配置中的Unitils 数据源
@SpringApplicationContext({"baobaotao-service.xml", "baobaotao-dao.xml"})
public class BaseServiceTest extends UnitilsJUnit4 {
@SpringApplicationContext
public ApplicationContext applicationContext;
@Test
public void testApplicationContext(){
assertNotNull(applicationContext);
}
}
public class SimpleUserServiceTest extends BaseServiceTest {
//通过ID 注入Bean
@SpringBean("userService")
private UserService userService1;
//通过类型注入Bean
@SpringBeanByType
private UserService userService2;
// 通过name 注入Bean
@SpringBeanByName
private UserService userService;
@Test
public void testApplicationContext(){
assertNotNull(applicationContext);
}
@Test
public void testUserService(){
assertNotNull(userService);
assertNotNull(userService1);
assertNotNull(userService2);
assertEquals("john123",userService.findUserByUserName("john123").getUserName());
assertEquals("john123",userService1.findUserByUserName("john123").getUserName());
assertEquals("john123",userService2.findUserByUserName("john123").getUserName());
}
}
通过@SpringApplicationContext 注解,首先扫描父类中的配置文件,如果找到了,就先加载父类的配置文件,然后加载子类重写的配置文件盒特定的配置文件,而且在父类中配置spring 配置文件,可以防治让子类的测试方法重复加载配置文件,这样spring应用上下文只会创建一次。
Hibernate 集成:
@HibernateSessionFactory("hibernate.cfg.xml")
//@SpringApplicationContext({"baobaotao-service.xml", "baobaotao-dao.xml"})
public class BaseDaoTest extends UnitilsJUnit4 {
// @SpringBean("sessionFactory")
@HibernateSessionFactory
public SessionFactory sessionFactory;
@Test
public void testSessionFactory(){
assertNotNull(sessionFactory);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- SQL方言,这边设定的是HSQL -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<!-- 数据源的名称 -->
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oralce:thin:@172.16.129.139:1521/qcjkdb</property>
<!--设置连接数据库的用户名-->
<property name="hibernate.connection.username">jtkfpt</property>
<!--设置连接数据库的密码-->
<property name="hibernate.connection.password">jtkfpt</property>
<!--设置显示sql语句方便调试-->
<property name="hibernate.show_sql">true</property>
<!-- 配置映射 -->
<property name="configurationClass">org.hibernate.cfg.AnnotationConfiguration</property>
<mapping class="com.baobaotao.domain.User" />
<mapping class="com.baobaotao.domain.LoginLog" />
</session-factory>
</hibernate-configuration>
最好将@HibernateSessionFactory 放入到父类中,防止子类重复加载配置文件。
集成DbUnit:
DAO层测试
利用DbUnit 模块可以从Excel、XML中读取数据写入数据库,简化数据库测试工作。下面以读取Excel数据为例:
public class MultiSchemaXlsDataSetFactory implements DataSetFactory {
protected String defaultSchemaName;
//初始化
public void init(Properties configuration, String defaultSchemaName) {
this.defaultSchemaName = defaultSchemaName;
}
//创建数据集
public MultiSchemaDataSet createDataSet(File... dataSetFiles) {
try {
MultiSchemaXlsDataSetReader xlsDataSetReader = new MultiSchemaXlsDataSetReader(
defaultSchemaName);
return xlsDataSetReader.readDataSetXls(dataSetFiles);
} catch (Exception e) {
throw new UnitilsException("创建数据集失败: "
+ Arrays.toString(dataSetFiles), e);
}
}
// 获取数据集文件的扩展名
public String getDataSetFileExtension() {
return "xls";
}
}
//EXCEL数据集读取器
public class MultiSchemaXlsDataSetReader {
private String defaultSchemaName;
public MultiSchemaXlsDataSetReader(String defaultSchemaName) {
this.defaultSchemaName = defaultSchemaName;
}
public MultiSchemaDataSet readDataSetXls(File... dataSetFiles) {
try {
Map<String, List<ITable>> tableMap = getTables(dataSetFiles);
MultiSchemaDataSet dataSets = new MultiSchemaDataSet();
for (Entry<String, List<ITable>> entry : tableMap.entrySet()) {
List<ITable> tables = entry.getValue();
try {
DefaultDataSet ds = new DefaultDataSet(tables
.toArray(new ITable[] {}));
dataSets.setDataSetForSchema(entry.getKey(), ds);
} catch (AmbiguousTableNameException e) {
throw new UnitilsException("构造DataSet失败!", e);
}
}
return dataSets;
} catch (Exception e) {
throw new UnitilsException("解析EXCEL文件出错:", e);
}
}
private Map<String, List<ITable>> getTables(File... dataSetFiles) {
Pattern pattern = Pattern.compile("\\.");
Map<String, List<ITable>> tableMap = new HashMap<String, List<ITable>>();
// 需要根据schema把Table重新组合一下
try {
for (File file : dataSetFiles) {
IDataSet dataSet = new XlsDataSet(new FileInputStream(file));
String[] tableNames = dataSet.getTableNames();
for (String each : tableNames) {
// 这个实际上不是schema, 是对应的spring的datasouceId
String schema = null;
String tableName;
String[] temp = pattern.split(each);
if (temp.length == 2) {
schema = temp[0];
tableName = temp[1];
} else {
schema = this.defaultSchemaName;
tableName = each;
}
ITable table = dataSet.getTable(each);
if (!tableMap.containsKey(schema)) {
tableMap.put(schema, new ArrayList<ITable>());
}
tableMap.get(schema).add(new XlsTable(tableName, table));
}
}
} catch (Exception e) {
throw new UnitilsException("创建数据集失败: "
+ Arrays.toString(dataSetFiles), e);
}
return tableMap;
}
//构造XslTable表
class XlsTable extends AbstractTable {
private ITable delegate;
private String tableName;
public XlsTable(String tableName, ITable table) {
this.delegate = table;
this.tableName = tableName;
}
public int getRowCount() {
return delegate.getRowCount();
}
public ITableMetaData getTableMetaData() {
ITableMetaData meta = delegate.getTableMetaData();
try {
return new DefaultTableMetaData(tableName, meta.getColumns(),
meta.getPrimaryKeys());
} catch (DataSetException e) {
throw new UnitilsException("Don't get the meta info from "
+ meta, e);
}
}
public Object getValue(int row, String column) throws DataSetException {
Object delta = delegate.getValue(row, column);
if (delta instanceof String) {
if (StringUtils.isEmpty((String) delta)) {
return null;
}
}
return delta;
}
}
}
以上是读取Excel数据的两个类,根据传入多个Excel文件,构造一个多数据集,一个数据集对应一个文件,其中一个sheet页代表一个table,最后将返回MultiSchemaDataSet类型对象,这样通过@DataSet注解,将自动解析Excel数据。
想利用上述数据集工厂类,需要在unitils.properties中配置数据集工厂类:
#配置数据集工厂
DbUnitModule.DataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory
DbUnitModule.ExpectedDataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory
此外还要配置unitils 测试环境。
unitils.properties
#启用的unitils 模块
unitils.modules=database,dbunit,hibernate,spring,
#配置扩展模块
#unitils.module.dbunit.className=sample.unitils.module.CustomExtModule
#database
#database.driverClassName=org.hsqldb.jdbcDriver
#database.url=jdbc:hsqldb:data/sampledb16;shutdown=true
#database.dialect = hsqldb
#database.userName=sa
#配置数据库连接
database.driverClassName=oracle.jdbc.driver.OracleDriver
database.url=jdbc:oracle:thin:@172.16.5.242:1521:orcl
database.dialect = org.hibernate.dialect.HSQLDialect
database.userName=unitils
database.schemaNames=UNITILS
The database maintainer is disabled by default.
#数据库维护策略 在每次运行时可更新数据库 根据dbMaintainer.script.locations设置的sql文件进行更新
#当以往文件改变 将更新此文件到数据库 未改变的sql文件将不变
#命名格式 <index>_<some name>.sql
updateDataBaseSchema.enabled=true
#This table is by default not created automatically
#数据库表生成策略
dbMaintainer.autoCreateExecutedScriptsTable=true
dbMaintainer.keepRetryingAfterError.enabled=true
dbMaintainer.script.locations=E:/data/Spring3.X/chapter16/resources/dbscripts
#配置数据集工厂
DbUnitModule.DataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory
DbUnitModule.ExpectedDataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory
#配置数据库加载策略
DbUnitModule.DataSet.loadStrategy.default=org.unitils.dbunit.datasetloadstrategy.impl.CleanInsertLoadStrategy
DatabaseModule.Transactional.value.default=commit
XSD generator
#配置数据集结构模式XSD生成路径
dataSetStructureGenerator.xsd.dirName=resources/xsd
数据库加载策略包括以下:
CleanInsertLoadStrategy: 每次先删除数据,再插入数据。
InsertLoadStrategy: 只插入数据。
RefreshLoadStrategy: 有同样key的数据更新,其他插入。
UpdateLoadStrategy: 有同样key的数据更新,其他不插入。
unitils-local.properties:
####################################
# Default configuration of Unitils #
####################################
unitils.module.spring.className=org.unitils.spring.SpringModule
unitils.module.spring.runAfter=database
unitils.module.spring.enabled=true
database.userName=unitils
database.password=unitils
database.schemaNames=UNITILS
#shcemaNames 必须为大写
#增加如下配置
org.unitils.core.dbsupport.DbSupport.implClassName=org.unitils.core.dbsupport.OracleDbSupport
org.dbunit.dataset.datatype.IDataTypeFactory.implClassName=org.dbunit.dataset.datatype.DefaultDataTypeFactory
测试数据库:
首先在数据库中建表,可以利用unitils.properties数据库表生成策略,指定sql文件来创建数据表。但其有严格的命名规则 <index>_<name>.sql
create table t_user (user_id number(38) not null,user_name varchar2(30),credits number(38),password varchar2(32),last_visit date,last_ip varchar2(23), primary key (user_id));
CREATE TABLE t_login_log (login_log_id number(38) not null, user_id number(38),ip VARCHAR2(23),login_datetime date,primary key (login_log_id));
用Excel准备测试数据:
测试类:
@Test
@DataSet("BaobaoTao.Users.xls")//??????
public void findUserByUserName() {
User user = userDao.findUserByUserName("tony");
user = userDao.findUserByUserName("jan");
assertEquals("jan", user.getUserName());
assertEquals("123456",user.getPassword());
assertEquals(new Long(10),user.getCredits());
}
简单的测试方法,通过@DataSet注解,unitils将利用
MultiSchemaXlsDataSetFactory解析数据,并利用数据加载策略,将Excel数据插入到数据库表中,先简单介绍,下节将重点介绍Service层、Web层测试,以上都是unitils 测试的基本介绍以及测试准备工作。
unitils: http://www.unitils.org/