`

单元测试系列之4:使用Unitils测试DAO层

 
阅读更多
   Spring 的测试框架为我们提供一个强大的测试环境,解决日常单元测试中遇到的大部分测试难题:如运行多个测试用例和测试方法时,Spring上下文只需创建一次;数据库现场不受破坏;方便手工指定Spring配置文件、手工设定Spring容器是否需要重新加载等。但也存在不足的地方,基本上所有的Java应用都涉及数据库,带数据库应用系统的测试难点在于数据库测试数据的准备、维护、验证及清理。Spring 测试框架并不能很好地解决所有问题。要解决这些问题,必须整合多方资源,如DbUnit、Unitils、Mokito等。其中Unitils正是这样的一个测试框架。

数据库测试的难点

    按照Kent Back的观点,单元测试最重要的特性之一应该是可重复性。不可重复的单元测试是没有价值的。因此好的单元测试应该具备独立性和可重复性,对于业务逻辑层,可以通过Mockito底层对象和上层对象来获得这种独立性和可重复性。而DAO层因为是和数据库打交道的层,其单元测试依赖于数据库中的数据。要实现DAO层单元测试的可重复性就需要对每次因单元测试引起数据库中的数据变化进行还原,也就是保护单元测试数据库的数据现场。

扩展Dbunit用Excel准备数据

    在测试数据访问层(DAO)时,通常需要经过测试数据的准备、维护、验证及清理的过程。这个过程不仅烦锁,而且容易出错,如数据库现场容易遭受破坏、如何对数据操作正确性进行检查等。虽然Spring测试框架在这一方面为我们减轻了很多工作,如通过事务回滚机制来保存数据库现场等,但对测试数据及验证数据准备方面还没有一种很好的处理方式。Unitils框架出现,改变了难测试DAO的局面,它将SpringModule、DatabaseModule、DbUnitModule等整合在一起,使得DAO的单元测试变得非常容易。基于Unitils框架的DAO测试过程如图16-6所示。



   以JUnit作为整个测试的基础框架,并采用DbUnit作为自动管理数据库的工具,以XML、Excel作为测试数据及验证数据准备,最后通过Unitils的数据集注解从Excel、XML文件中加载测试数据。使用一个注解标签就可以完成加载、删除数据操作。由于XML作为数据集易用性不如Excel,在这里就不对XML数据集进行讲解。下面我们主要讲解如何应用Excel作为准备及验证数据的载体,减化DAO单元测试。由于Unitils没有提供访问Excel的数据集工厂,因此需要编写插件支持Excel格式数据源。Unitils提供一个访问XML的数据集工厂MultiSchemaXmlDataSetFactory,其继承自DbUnit提供的数据集工厂接口DataSetFactory。我们可以参考这个XML数据集工厂类,编写一个访问Excel的数据集工厂MultiSchemaXlsDataSetFactory及Excel数据集读取器MultiSchemaXlsDataSetReader,然后在数据集读取器中调用Apache POI类库来读写Excel文件。如代码清单16-20所示。

import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;
…
public class MultiSchemaXlsDataSetFactory implements DataSetFactory {
protected String defaultSchemaName;

	//① 初始化数据集工厂
	public void init(Properties configuration, String defaultSchemaName) {
		this.defaultSchemaName = defaultSchemaName;
	}

	//② 从Excel文件创建数据集
	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";
	}
}
…


   与XML数据集工厂MultiSchemaXmlDataSetFactory一样,Excel的数据集工厂也需要实现数据集工厂接口DataSetFactory的三个方法:init(…)、createDataSet(File... dataSetFiles)、getDataSetFileExtension()。在①处,初始化数据集工厂,需要设置一个默认的数据库表模式名称defaultSchemaName。在②处,执行创建多数据集,具体读取构建数据集的过程封装在Excel读取器MultiSchemaXlsDataSetReader中。在③处,获取数据集文件的扩展名,对Excel文件而言就是“xls”。下面来看一下这个数据集读取器的实现代码。

import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;
…
// Excel数据集读取器
public class MultiSchemaXlsDataSetReader {
	private String defaultSchemaName;
	
	public MultiSchemaXlsDataSetReader(String defaultSchemaName) {
		this.defaultSchemaName = defaultSchemaName;
	}	
     // Excel数据集读取器
	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);
		}
	}
…
}
…


  根据传入的多个Excel文件,构造一个多数据集。 其中一个数据集对应一个Excel文件,一个Excel的Sheet表对应一个数据库Table。通过DbUnit提供Excel数据集构造类XlsDataSet,可以很容易将一个Excel文件转换为一个数据集:XlsDataSet(new FileInputStream(xlsFile))。最后将得到的多个DataSet用MultiSchemaDataSet进行封装。
  下面就以一个用户DAO的实现类WithoutSpringUserDaoImpl为例,介绍如何使用我们实现的Excel数据集工厂。为了让Unitils使用自定义的数据集工厂,需要在unitils.properties配置文件中指定自定义的数据集工厂。

引用

DbUnitModule.DataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory
DbUnitModule.ExpectedDataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory


  其中DbUnitModule.DataSet.factory.default是配置数据集工厂类,在测试方法中可以使用@DataSet注解加载指定的准备数据。默认是XML数据集工厂,这里指定自定义数据集工厂类全限定名为  sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory。
  其中DbUnitModule. ExpectedDataSet.factory.default是配置验证数据集工厂类,也是指定自定义数据集工厂类,使用@ ExpectedDataSet注解加载验证数据。

import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;
…
public class UserDaoTest extends UnitilsJUnit4 {
	@Test
	@DataSet //① 准备测试数据
	public void getUser() {
		…
	}

	@Test
	@DataSet("BaobaoTao.SaveUser.xls") //② 准备测试数据 -
	@ExpectedDataSet //③ 准备验证数据
	public void saveUser()throws Exception  {
		…
	}

}
…


  @DateSet 注解表示了测试时需要寻找DbUnit的数据集文件进行加载,如果没有指明数据集的文件名,则Unitils自动在当前测试用例所在类路径下加载文件名为测试用例类名的数据集文件,实例中①处,将到UserDaoTest.class所在目录加载WithExcelUserDaoTest.xls 数据集文件。
  @ExpectedDataSet注解用于加载验证数据集文件,如果没有指明数据集的文件名,则会在当前测试用例所在类路径下加载文件名为testClassName.methodName-result.xls的数据集文件。实例中③处将加载UserDaoTest. saveUser.result.xls数据集文件。

测试实战

  使用JUnit作为基础测试框架,结合Unitils、DbUnit管理测试数据,并使用我们编写的Excel数据集工厂(见代码清单16 20)。从Excel数据集文件中获取准备数据及验证数据,并使用HSQLDB作为测试数据库。下面详细介绍如何应用Excel准备数据集及验证数据集来测试DAO。
  在进行DAO层的测试之前,我们先来认识一下需要测试的UserDaoImpl用户数据访问类。UserDaoImpl用户数据访问类中拥有一个获取用户信息和保存注册用户信息的方法,其代码如下所示。

import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.HibernateTemplate;
import com.baobaotao.dao.UserDao;
import com.baobaotao.domain.User;
public class UserDaoImpl implements UserDao {

    //通过用户名获取用户信息
public User findUserByUserName(String userName) {
		String hql = " from User u where u.userName=?";
		List<User> users = getHibernateTemplate().find(hql, userName);
		if (users != null && users.size() > 0)
			return users.get(0);
		else
			return null;
	}

//保存用户信息
	public void save(User user) {
		getHibernateTemplate().saveOrUpdate(user);
	}
	…
}


  我们认识了需要测试的UserDaoImpl用户数据访问类之后,还需要认识一下用于表示用户领域的对象User,在演示测试保存用户信息及获取用户信息时需要用到此领域对象,其代码如下所示。

import javax.persistence.Column;
import javax.persistence.Entity;
…
@Entity
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Table(name = "t_user")
public class User implements Serializable{
	@Id
	@Column(name = "user_id")
	protected int userId;

	@Column(name = "user_name")
	protected String userName;

	protected String password;

	@Column(name = "last_visit")
	protected Date lastVisit;

	@Column(name = "last_ip")
	protected String lastIp;

	@Column(name = "credits")
	private int credits;
	…
}

  用户登录日志领域对象LoginLog与用户领域对象Hibernate注解配置一致,这里就不再列出,读者可以参考本书附带光盘中的实例代码。在实例测试中,我们直接使用Hibernate进行持久化操作,所以还需要对Hibernate进行相应配置,详细的配置清单如下所示。
<?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">org.hsqldb.jdbcDriver</property>
		<property name="hibernate.connection.url">
jdbc:hsqldb:data/sampledb
</property>
		<!--设置连接数据库的用户名-->
		<property name="hibernate.connection.username">sa</property>
		<!--设置连接数据库的密码-->
		<property name="hibernate.connection.password"></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>


  选用HSQLDB作为测试数据库,在①处,配置HSQLDB的SQL方言HSQLDialect。在②处,对连接数据库驱动及数据库连接进行相应的配置。为了方便测试调试,在③处设置显示Hibernate生成的SQL语句。在④处启用Hibernate的注解功能,并配置相应的领域对象,如实例中的User、LoginLog。将配置好的hibernate.cfg.xml放在src目录下。

配置Unitils测试环境

  要在单元测试中更好地使用Unitils ,首先需要在测试源码的根目录中创建一个项目级unitils.properties 配置文件,实例中unitils.properties详细配置清单如下所示。
#① 启用unitils所需模块 
unitils.modules=database,dbunit,hibernate,spring

#自定义扩展模块,详见实例源码
unitils.module.dbunit.className=sample.unitils.module.CustomExtModule

#② 配置数据库连接
database.driverClassName=org.hsqldb.jdbcDriver
database.url=jdbc:hsqldb:data/sampledb;shutdown=true
database.userName=sa
database.password=
database.schemaNames=public
database.dialect = hsqldb

#③ 配置数据库维护策略.
updateDataBaseSchema.enabled=true

#④ 配置数据库表创建策略
dbMaintainer.autoCreateExecutedScriptsTable=true
dbMaintainer.script.locations=D:/masterSpring/chapter16/resources/dbscripts

#⑤ 数据集加载策略
#DbUnitModule.DataSet.loadStrategy.default=org.unitils.dbunit.datasetloadstrategy.InsertLoadStrategy 


#⑥ 配置数据集工厂
DbUnitModule.DataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory
DbUnitModule.ExpectedDataSet.factory.default=sample.unitils.dataset.excel.MultiSchemaXlsDataSetFactory

#⑦ 配置事务策略
DatabaseModule.Transactional.value.default=commit

#⑧ 配置数据集结构模式XSD生成路径
dataSetStructureGenerator.xsd.dirName=resources/xsd


   我们知道unitils.properties中配置的属性是整个项目级别的,整个项目都可以使用这些全局的属性配置。特定用户使用的属性可以设置在unitils-local.properties 文件中,比如user、password和schema,这样每个开发者就使用自定义的测试数据库的schema,而且彼此之间也不会产生影响,实例的详细配置清单如下所示。

…
database.userName=sa
database.password=
database.schemaNames=public
…

  
   如果用户分别在unitils.properties文件及unitils -local.properties文件中对相同属性配置不同值时,将会以unitils-local.properties 配置内容为主。如在unitils.properties配置文件中,也配置了database.schemaNames=xxx,测试时启用的是用户自定义配置中的值database.schemaNames=public。

   默认的数据集加载机制采用先清理后插入的策略,也就是数据在被写入数据库的时候是会先删除数据集中有对应表的数据,然后将数据集中的数据写入数据库。这个加载策略是可配置的,我们可以通过修改DbUnitModule.DataSet.loadStrategy.default的属性值来改变加载策略。如实例代码清单16 27中⑤配置策略,这时加载策略就由先清理后插入变成了插入,数据已经存在表中将不会被删除,测试数据只是进行插入操作。可选的加载策略列表如下所示。
  •   CleanInsertLoadStrategy:先删除dateSet中有关表的数据,然后再插入数据。
  •   InsertLoadStrategy:只插入数据。
  •   RefreshLoadStrategy:有同样key的数据更新,没有的插入。
  •   UpdateLoadStrategy: 有同样key的数据更新,没有的不做任何操作。


配置事务策略

   在测试DAO的时候都会填写一些测试数据,每个测试运行都会修改或者更新了数据,当下一个测试运行的时候,都需要将数据恢复到原有状态。如果使用的是Hibernate或者JPA,需要每个测试都运行在事务中,保证系统的正常工作。默认情况下,事务管理是disabled的,我们可以通过修改DatabaseModule.Transactional.value.default配置选项,如实例代码清单16 27中⑧配置策略,这时每个测试都将执行commit,其他可选的配置属性值有rollback和disabled。

准备测试数据库及测试数据

   配置好了Unitils基本配置、加载模块、数据集创建策略、事务策略之后,我们就着手开始测试数据库及测试数据准备工作,首先我们创建测试数据库。

创建测试数据库

   在源码包根目录下创建一个dbscripts文件夹(文件夹目录结构如图16-7所示),且这个文件夹必须与在unitils.properties 文件中dbMaintainer.script.locations配置项指定的位置一致,如代码清单16 27中④ 所示。

   在这个文件夹中创建一个数据库创建脚本文件001_create_sampledb.sql,里面包含创建用户表t_user 及登录日志表t_login_log,详细的脚本如下所示。
CREATE TABLE t_user (
user_id INT generated by default as identity (start with 100),
user_name VARCHAR(30),credits INT,
password  VARCHAR(32),last_visit timestamp,
last_ip  VARCHAR(23), primary key (user_id));

CREATE TABLE t_login_log (
login_log_id  INT generated by default as identity (start with 1), 
user_id   INT,
ip  VARCHAR(23),
login_datetime timestamp,
primary key (login_log_id));

   细心的读者可能会发现这个数据库创建脚本文件名好像存在一定的规则,是的,这个脚本文件命名需要按以下规则命名:版本号 +  “_” +  “自定义名称” + “ .sql” 。

连接到测试数据库

   测试DAO时,读者要有个疑问,测试数据库用到的数据源来自哪里,怎么让我们测试的DAO类来使用我们的数据源。执行测试实例的时候,Unitils 会根据我们定义的数据库连接属性来创建一个数据源实例连接到测试数据库。随后的DAO测试会重用相同的数据源实例。建立连接的细节定义在unitils.properties配置文件中,如代码清单16 27中的② 配置部分所示。

用Excel准备测试数据

   准备好测试数据库之后,剩下的工作就是用Excel来准备测试数据及验证数据,回顾一下我们要测试的UserDaoImpl 类(代码清单16 24),需要对其中的获取用户信息方法findUserByUserName()及保存用户信息方法saveUser()进行测试,所以我们至少需要准备三个Excel数据集文件 ,分别是供查询用户用的数据集BaobaoTao.Users.xls、供保存用户信息用的数据集BaobaoTao.SaveUser.xls及供保存用户信息用的验证数据集BaobaoTao. ExpectedSaveUser.xls。下面以用户数据集BaobaoTao.Users.xls实例进行说明,如图16-8所示。


   在①处t_user表示数据库对应的表名称。在②处表示数据库中t_user表对应的字段名称。在③处表示准备测试的模拟数据。一个数据集文件可以对应多张表,一个Sheet对就一张表。把创建好的数据集文件放到与测试类相同的目录中,如实例中的UserDaoTest类位于com.baobaotao.dao包中,则数据集文件需要放到当前包中。其他两个数据集文件数据结构如图16-9和16-10所示。



编写UserDaoImpl的测试用例

  完成了Unitils环境配置、准备测试数据库及测试数据之后,就可以开始编写用户DAO单元测试类,下面我们为用户数据访问UserDaoImpl编写测试用例类。
import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;
…
@SpringApplicationContext( {"baobaotao-dao.xml" }) //① 初始化Spring容器
public class UserDaoTest extends UnitilsJUnit4 {

	@SpringBean("jdbcUserDao")  //② 从Spring容器中加载DAO
	private UserDao userDao;    

	@Before
	public void init() {
		
	}
   …
}

   在①处,通过Unitils提供@ SpringApplicationContext注解加载Spring配置文件,并初始化Spring容器。在②处,通过@SpringBean注解从Spring容器加载一个用户DAO实例。编写UserDaoTest测试基础模型之后,接下来就编写查询用户信息findUserByUserName()的测试方法。

代码清单16 31 UserDaoTest.findUserByUserName()测试
import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;
…
public class UserDaoTest extends UnitilsJUnit4 {
…

@Test //① 标志为测试方法
	@DataSet("BaobaoTao.Users.xls") //② 加载准备用户测试数据
	public void findUserByUserName() {
		User user = userDao.findUserByUserName("tony"); //③ 从数据库中加载tony用户
		assertNull("不存在用户名为tony的用户!", user);
		user = userDao.findUserByUserName("jan"); //④ 从数据库中加载jan用户
		assertNotNull("jan用户存在!", user);
		assertEquals("jan", user.getUserName());
	    assertEquals("123456",user.getPassword());
		assertEquals(10,user.getCredits());	
    }
…
}


   在①处,通过JUnit提供@Test注解,把当前方法标志为可测试方法。在②处,通过Unitils提供的@DataSet注解从当前测试类UserDaoTest.class所在的目录寻找支持DbUnit的数据集文件并进行加载。执行测试逻辑之前,会把加载的数据集先持久化到测试数据库中,具体加载数据集的策略详见上文“配置数据集加载策略”部分。实例中采用的默认加载策略,即先删除测试数据库对应表的数据再插入数据集中的测试数据。这种策略可以避免不同测试方法加载数据集相互干扰。在③处执行查询用户方法时,测试数据库中t_user表数据已经是如图16-8 BaobaoTao.Users.xls所示的数据,因此查询不到“tony”用户信息。在④处,执行查询“jan”用户信息,从测试数据集可以看出,可以加载到“jan”的详细信息。最后在IDE中执行UserDaoTest. findUserByUserName()测试方法,按我们预期通过测试,测试结果如图16-11所示。


   完成了查询用户的测试之后,我们开始着手编写保存用户信息的测试方法,详细的实现代码如下所示。
import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;
…
public class UserDaoTest extends UnitilsJUnit4 {
…	

@Test  //① 标志为测试方法
	@ExpectedDataSet("BaobaoTao.ExpectedSaveUser.xls") //准备验证数据
	public void saveUser()throws Exception  {
		User u = new User();
		u.setUserId(1);
		u.setUserName("tom");
		u.setPassword("123456");
		u.setLastVisit(getDate("2011-06-06 08:00:00","yyyy-MM-dd HH:mm:ss"));
		u.setCredits(30);
		u.setLastIp("127.0.0.1");
		userDao.save(u);  //执行用户信息更新操作
	}
…
}

   在①处,通过JUnit提供@Test注解,把当前方法标志为可测试方法。在②处,通过Unitils提供的@ExpectedDataSet注解从当前测试类UserDaoTest.class所在的目录寻找支持DbUnit的验证数据集文件并进行加载,之后验证数据集里的数据和数据库中的数据是否一致。在UserDaoTest.saveUser()测试方法中创建一个User实例,并设置与图16-10 验证数据集中相同的数据,然后执行保存用户操作。最后在IDE中执行UserDaoTest.saveUser()测试方法,执行结果如图16-12所示。

   虽然已经成功完成了保存用户信息UserDaoTest.saveUser() 方法测试,但还是存在不足的地方,我们测试数据通过硬编码方式直接设置在User实例中。如果需要更改测试数据,只能更改测试代码。大大削减了测试的灵活性。如果能直接从Excel数据集获取测试数据,并自动绑定到目标对象,那我们的测试用例就更加完美。为此笔者编写了一个获取Excel数据集Bean工厂XlsDataSetBeanFactory,用于自动绑定数据集到测试对象。我们对上面的测试方法进行整改,实现代码如代码清单16-33所示。
import org.unitils.core.UnitilsException;
import org.unitils.DbUnit.datasetfactory.DataSetFactory;
import org.unitils.DbUnit.util.MultiSchemaDataSet;
import sample.unitils.dataset.util.XlsDataSetBeanFactory;
…
public class UserDaoTest extends UnitilsJUnit4 {
…	

@Test  //① 标志为测试方法
	@ExpectedDataSet("BaobaoTao.ExpectedSaveUser.xls") //准备验证数据
	public void saveUser()throws Exception  {

//② 从保存数据集中创建Bean
	User u  = XlsDataSetBeanFactory.createBean("BaobaoTao.SaveUser.xls” 
,"t_user", User.class);
		userDao.save(u); //③ 执行用户信息更新操作
	}
…
}

   在②处,通过XlsDataSetBeanFactory.createBean()方法,从当前测试类所在目录加载BaobaoTao.SaveUser.xls数据集文件,其数据结构如图16-9所示。把BaobaoTao.SaveUser.xls中名称为t_user 的Sheet页中的数据绑定到User对象,如果当前Sheet页有多条记录,可以通过XlsDataSetBeanFactory.createBeans()获取用户列表List<User>。最后在IDE中重新执行UserDaoTest.saveUser()测试方法,执行结果如图16-13所示。

   从测试结果可以看出,执行UserDaoTest.saveUser()测试失败。从右边的失败报告信息我们可以看出,是由于模拟用户的积分与我们期望数据不一致造成,期望用户积分是30,而我们保存用户的积分是10。重新对比一下图16-9 BaobaoTao.SaveUser.xls数据集数据与图16-10 BaobaoTao.ExpectedSaveUser.xls数据集的数据,确实我们准备保存数据集的数据与验证结果的数据不一致。把BaobaoTao.SaveUser.xls数据集中的用户积分更改为30,最后在IDE中重新执行UserDaoTest.saveUser()测试方法,执行结果如图16-14所示。

   从测试结果可以看出,保存用户通过测试。从上述的测试实战,我们已经体验到用Excel准备测试数据与验证数据带来的便捷性。到此,我们完成了DAO测试的整个过程,对于XlsDataSetBeanFactory具体实现,读者可以查看本章的实例源码,这里就不做详细分析。下面是实现基本骨架。
import org.dbunit.dataset.Column;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.excel.XlsDataSet;
…	
public class XlsDataSetBeanFactory {
	
	//从Excel数据集文件创建多个Bean
	public static <T> List<T> createBeans(String file, String tableName,
			Class<T> clazz) throws Exception {
		BeanUtilsBean beanUtils = createBeanUtils();
		List<Map<String, Object>> propsList = createProps(file, tableName);
		List<T> beans = new ArrayList<T>();
		for (Map<String, Object> props : propsList) {
			T bean = clazz.newInstance();
			beanUtils.populate(bean, props);
			beans.add(bean);
		}
		return beans;
	}

	//从Excel数据集文件创建多个Bean
	public static <T> T createBean(String file, String tableName, Class<T> clazz)
			throws Exception {
		BeanUtilsBean beanUtils = createBeanUtils();
		List<Map<String, Object>> propsList = createProps(file, tableName);
		T bean = clazz.newInstance();
		beanUtils.populate(bean, propsList.get(0));
		return bean;
	}
…
}



  这些文章摘自于我的《Spring 4.x企业应用开发实战》的第16章,我将通过连载的方式,陆续在此发出。欢迎大家讨论。
  • 大小: 38 KB
  • 大小: 23.1 KB
  • 大小: 78.7 KB
  • 大小: 123 KB
  • 大小: 55.2 KB
  • 大小: 52.6 KB
  • 大小: 75.9 KB
  • 大小: 51.2 KB
分享到:
评论
10 楼 luhouxiang 2016-01-10  
我这儿总是报这样的错误,没摸着头绪:
org.unitils.core.UnitilsException: Unable to create application context for locations [classpath:/baobaotao-dao.xml]
at org.unitils.spring.util.ApplicationContextManager.createInstanceForValues(ApplicationContextManager.java:121)
at org.unitils.spring.util.ApplicationContextManager.createInstanceForValues(ApplicationContextManager.java:36)
at
...
9 楼 luhouxiang 2016-01-07  
mark 一下
8 楼 accplxmaccplxm 2015-05-12  
我也遇到2 楼 同样的问题,请问是何原因??
7 楼 AlittleOne 2014-07-31  
有代码下载地址吗?
6 楼 SunSteven 2013-12-18  
在执行测试数据比较的时候,因为数据在Hibernate采用自增的方式,所以在Excel写的主键没有作用,在关联表的时候,因为要引用ID,所以必须要注意把数据库值情况,以便它从1开始增加。我认为最好测试的时候,在Hibernate注解中把数据库的自增修改为从Excel业务数据中去。

另外有一个问题,从数据集取得数据的时候,因为无主键,必须要把ExpectedSaveUser.xls中的id列删掉,否则无法通过测试。是否有其他办法?
5 楼 SunSteven 2013-12-18  
已经自行解决,解决方法如下

我制作了一个空的表,BaobaoTao.SaveBoardsnull.xls;这个表的格式与BaobaoTao.SaveBoards.xls格式都一样,但是没有数据。
然后把方法上的内容增加一个@DataSet,
    @Test
    @DataSet("BaobaoTao.SaveBoardsnull.xls")
    @ExpectedDataSet("BaobaoTao.ExpectedBoards.xls")
然后进行单元测试,发现另外1个问题
第一个问题:在db中有5行空数据;解决方法:将Excel表中的空行在全部Delete一次。这样Db中就没有空行数据,就解决了这个问题。

(或者你写的那个读取Excel的单元格的方法,重新判断一次是否有空的的内容,如空,就不插入数据库中)

但测试依然没有通过,通过报错发现,在BaobaoTao.ExpectedBoards.xls上,有id,但是从db中读出的内容中,并没有包含数据库的id,所以发现缺这个id,于是,把BaobaoTao.ExpectedBoards.xls上的id删掉,测试就成功了。在测试的进行数据比较的时候,他是不比较id的。

4 楼 SunSteven 2013-12-17  
问题:
1、现象:BoardDaoTest 在第一次执行@ExpectDataSet的方法时候(public void addBoard()throws Exception ),数据表没有被初始化。所以产生插入错误。这个在他的帮助文件中好像没有找到。但是如果是@dataSet的标签的话,数据表就会被初始化。


问题:有无办法在执行@expectDataSet的方法时候,数据表也能进行初始化

2、现象:在UserDaoTest中的第二个方法saveUser,也是@ExpectDataSet这个便签,发现他并没有Delete数据库中的数据。导致数据库中原有数据任然存在。

问题:如果让@ExpectDataSet能够发挥作用,让他能执行delete数据表中记录作用。(如果这个方法标注@DataSet,就可以删除掉这个数据记录。
3 楼 stamen 2013-12-17  
重先运行 dbunit会重新清除原有的数据啊,不过这个行为可以通过更改unitils的配置文件更改,你可以看一下unitils的配置文件的属性,附件是一个默认的unitils,每个参数都有详细的解释,你看下,并更改相应的属性。
引用

####################################
# Default configuration of Unitils #
####################################

# This file contains default configuration values for unitils. This file should not be edited.
# All properties in this file can be overridden, either in the project specific properties file
# (unitils.properties) or in the local properties file (configured by unitils.configuration.customFileName).

# Name or path of the project specific properties file. The system will try to find this file in the classpath (recommended),
# the user home folder or the local filesystem
unitils.configuration.customFileName=unitils.properties
# Name or path of the user specific properties file. This file may contain the necessary parameters to connect to the
# developer's own unit test schema. It is recommended to override the name of this file in the project specific properties
# file, to include the name of the project. The system will try to find this file in the classpath, the user home folder
# (recommended) or the local filesystem.
unitils.configuration.localFileName=unitils-local.properties

# List of modules that is loaded. Overloading this list is normally not useful, unless you want to add a custom
# module. Disabling a module can be performed by setting unitils.module.<modulename>.enabled to false.
# If a module's specific dependencies are not found (e.g. hibernate is not in you classpath), this module is not loaded,
# even if it is in this list and the enabled property is set to true. It's therefore not strictly necessary to disable
# any of these modules.
unitils.modules=database,dbunit,hibernate,mock,easymock,inject,spring,jpa,io

#### Unitils core configuration ###
# For each module, the implementation class is listed in unitils.module.<modulename>.className, the sequence of the
# execution of their code is influenced by unitils.module.<modulename>.runAfter. Disabling a module can be performed by
# setting unitils.module.<modulename>.enabled to false.
unitils.module.database.className=org.unitils.database.DatabaseModule
unitils.module.database.runAfter=
unitils.module.database.enabled=true

unitils.module.hibernate.className=org.unitils.orm.hibernate.HibernateModule
unitils.module.hibernate.runAfter=
unitils.module.hibernate.enabled=true

unitils.module.dbunit.className=org.unitils.dbunit.DbUnitModule
unitils.module.dbunit.runAfter=
unitils.module.dbunit.enabled=true

unitils.module.mock.className=org.unitils.mock.MockModule
unitils.module.mock.runAfter=
unitils.module.mock.enabled=true

unitils.module.easymock.className=org.unitils.easymock.EasyMockModule
unitils.module.easymock.runAfter=
unitils.module.easymock.enabled=true

unitils.module.inject.className=org.unitils.inject.InjectModule
unitils.module.inject.runAfter=
unitils.module.inject.enabled=true

unitils.module.spring.className=org.unitils.spring.SpringModule
unitils.module.spring.runAfter=database
unitils.module.spring.enabled=true

unitils.module.jpa.className=org.unitils.orm.jpa.JpaModule
unitils.module.jpa.runAfter=
unitils.module.jpa.enabled=true

unitils.module.io.className=org.unitils.io.IOModule
unitils.module.io.runAfter=
unitils.module.io.enabled=true


### DatabaseModule Configuration ###

## Full qualified class name of an implementation of org.unitils.database.config.DataSourceFactory. This class is used
# to provide a DataSource for all database unit tests and for the DBMaintainer.
org.unitils.database.config.DataSourceFactory.implClassName=org.unitils.database.config.PropertiesDataSourceFactory

# Properties for the PropertiesDataSourceFactory
database.driverClassName=
database.url=
database.userName=
database.password=

# This property specifies the underlying DBMS implementation. Supported values are 'oracle', 'db2', 'mysql', 'hsqldb',
# 'postgresql', 'derby' and 'mssql'. The value of this property defines which vendor specific implementations of
# DbSupport and ConstraintsDisabler are chosen.
database.dialect=

# A comma-separated list of all used database schemas. The first schema name is the default one, if no schema name is
# specified in for example a dbunit data set, this default one is used.
# A schema name is case sensitive if it's surrounded by database identifier quotes (e.g. " for oracle)
database.schemaNames=


### DatabaseModule's DbMaintainer configuration ###

# If set to true, the DBMaintainer will be used to update the unit test database schema. This is done once for each
# test run, when creating the DataSource that provides access to the unit test database.
updateDataBaseSchema.enabled=false

# Indicates the database must be recreated from scratch when an already executed script is updated. If false, the
# DBMaintainer will give an error when an existing script is updated.
dbMaintainer.fromScratch.enabled=true
# Indicates whether a from scratch update should be performed when the previous update failed, but
# none of the scripts were modified since that last update. If false a new update will be tried only when
# changes were made to the script files.
dbMaintainer.keepRetryingAfterError.enabled=false

# Fully qualified classnames of implementations of org.unitils.core.dbsupport.DbSupport.
org.unitils.core.dbsupport.DbSupport.implClassName.oracle=org.unitils.core.dbsupport.OracleDbSupport
org.unitils.core.dbsupport.DbSupport.implClassName.oracle9=org.unitils.core.dbsupport.Oracle9DbSupport
org.unitils.core.dbsupport.DbSupport.implClassName.oracle10=org.unitils.core.dbsupport.Oracle10DbSupport
org.unitils.core.dbsupport.DbSupport.implClassName.hsqldb=org.unitils.core.dbsupport.HsqldbDbSupport
org.unitils.core.dbsupport.DbSupport.implClassName.mysql=org.unitils.core.dbsupport.MySqlDbSupport
org.unitils.core.dbsupport.DbSupport.implClassName.db2=org.unitils.core.dbsupport.Db2DbSupport
org.unitils.core.dbsupport.DbSupport.implClassName.postgresql=org.unitils.core.dbsupport.PostgreSqlDbSupport
org.unitils.core.dbsupport.DbSupport.implClassName.derby=org.unitils.core.dbsupport.DerbyDbSupport
org.unitils.core.dbsupport.DbSupport.implClassName.mssql=org.unitils.core.dbsupport.MsSqlDbSupport


# Determines how the database stores non-quoted identifiers (with identifiers, we mean names for tables, columns, etc.)
# Possible values are lower_case, upper_case, mixed_case and auto
# If auto is specified, the database metadata is used to determine the correct value
database.storedIndentifierCase.oracle=auto
database.storedIndentifierCase.hsqldb=auto
database.storedIndentifierCase.mysql=auto
database.storedIndentifierCase.db2=auto
database.storedIndentifierCase.postgresql=auto
database.storedIndentifierCase.derby=auto
database.storedIndentifierCase.mssql=auto

# Determines the string the database uses to quote identifiers, i.e. make them case-sensitive
# (with identifiers, we mean names for tables, columns, etc.)
# Leave empty if quoting is not supported.
# If auto is specified, the database metadata is used to determine the correct value
database.identifierQuoteString.oracle=auto
database.identifierQuoteString.hsqldb=auto
database.identifierQuoteString.mysql=auto
database.identifierQuoteString.db2=auto
database.identifierQuoteString.postgresql=auto
database.identifierQuoteString.derby=auto
database.identifierQuoteString.mssql=auto


# Fully qualified name of the implementation of org.unitils.dbmaintainer.maintainer.version.ExecutedScriptInfoSource that is used.
# The default value is 'org.unitils.dbmaintainer.maintainer.version.ExecutedScriptInfoSource', which retrieves the database version
# from the updated database schema itself. Another implementation could e.g. retrieve the version from a file.
org.unitils.dbmaintainer.version.ExecutedScriptInfoSource.implClassName=org.unitils.dbmaintainer.version.impl.DefaultExecutedScriptInfoSource
# Name of the table that contains the database update script that have already been executed on the database.
dbMaintainer.executedScriptsTableName=dbmaintain_scripts
# Name of the column in which the name of the executed script file is stored
dbMaintainer.fileNameColumnName=file_name
dbMaintainer.fileNameColumnSize=150
# Name of the column in which the version index string of the executed script is stored.
dbMaintainer.versionColumnName=version
dbMaintainer.versionColumnSize=25
# Name of the column in which the last modification date of the executed script file is stored.
dbMaintainer.fileLastModifiedAtColumnName=file_last_modified_at
# Name of the column in which the checksum of the content of the script is stored.
dbMaintainer.checksumColumnName=checksum
dbMaintainer.checksumColumnSize=50
# Name of the column that stores the timestamp at which the script was executed
dbMaintainer.executedAtColumnName=executed_at
dbMaintainer.executedAtColumnSize=20
# Name of the column in which is stored whether the script ran without error or not.
dbMaintainer.succeededColumnName=succeeded
# Set this property to true if the dbmaintain_scripts table should be created automatically if not found.
# If false, an exception is thrown when the table is not found, indicating how to create it manually.
# This property is false by default to be sure that a database is cleared by accident. If an executed
# scripts table is available, we assume it to be a database managed by dbmaintain.
dbMaintainer.autoCreateExecutedScriptsTable=false
dbMaintainer.timestampFormat=yyyy-MM-dd HH:mm:ss

# Fully qualified name of the implementation of org.unitils.dbmaintainer.maintainer.script.ScriptSource that is used.
# The default value is 'org.unitils.dbmaintainer.maintainer.script.FileScriptSource', which will retrieve the scripts
# from the local file system.
org.unitils.dbmaintainer.script.ScriptSource.implClassName=org.unitils.dbmaintainer.script.impl.DefaultScriptSource
# Defines where the scripts can be found that must be executed on the database. Multiple locations may be
# configured, separated by comma's. A script location can be a folder or a jar file.
dbMaintainer.script.locations=
# Extension of the files containing the database update scripts
dbMaintainer.script.fileExtensions=sql,ddl
# Comma separated list of directories and files in which the post processing database scripts are
# located. Directories in this list are recursively search for files.
dbMaintainer.postProcessingScript.directoryName=postprocessing

# Defines whether script last modification dates can be used to decide that it didn't change. If set to true,
# the dbmaintainer will decide that a file didn't change since the last time if it's last modification date hasn't
# changed. If it did change, it will first calculate the checksum of the file to verify that the content really
# changed. Setting this property to true improves performance: if set to false the checksum of every script must
# be calculated for each run of the dbmaintainer. It's advised to set this property to true when using the dbmainainer
# to update a unit test database. For applying changes to an environment that can only be updated incrementally (e.g.
# a database use by testers or even the production database), this parameter should be false, since working with last
# modification dates is not guaranteed to be 100% bulletproof (although unlikely, it is possible that a different
# version of the same file is checked out on different systems on exactly the same time).
dbMaintainer.useScriptFileLastModificationDates.enabled=true

# Fully qualified name of the implementation of org.unitils.dbmaintainer.script.ScriptRunner that is used. The
# default value is 'org.unitils.dbmaintainer.script.SQLScriptRunner', which executes a regular SQL script.
org.unitils.dbmaintainer.script.ScriptRunner.implClassName=org.unitils.dbmaintainer.script.impl.DefaultScriptRunner
# Fully qualified classname of the implementation of org.unitils.dbmaintainer.script.ScriptParser
org.unitils.dbmaintainer.script.ScriptParser.implClassName=org.unitils.dbmaintainer.script.impl.DefaultScriptParser
org.unitils.dbmaintainer.script.ScriptParser.implClassName.oracle=org.unitils.dbmaintainer.script.impl.OracleScriptParser
org.unitils.dbmaintainer.script.ScriptParser.implClassName.oracle9=org.unitils.dbmaintainer.script.impl.OracleScriptParser
org.unitils.dbmaintainer.script.ScriptParser.implClassName.oracle10=org.unitils.dbmaintainer.script.impl.OracleScriptParser
# Set to true if characters can be escaped by using backslashes. For example '\'' instead of the standard SQL way ''''.
# Note this is not standard SQL behavior and is therefore disabled by default.
org.unitils.dbmaintainer.script.ScriptParser.backSlashEscapingEnabled=false

# If set to true, an implementation of org.unitils.dbmaintainer.constraints.ConstraintsDisabler will be used to disable
# the foreign key and not null constraints of the unit test database schema.
# The ConstraintsDisabler is configured using the properties specified below. The property with key 'database.dialect'
# specifies which implementation is used.
dbMaintainer.disableConstraints.enabled=true
# Fully qualified classname of the implementation of org.unitils.dbmaintainer.structure.ConstraintsDisabler
org.unitils.dbmaintainer.structure.ConstraintsDisabler.implClassName=org.unitils.dbmaintainer.structure.impl.DefaultConstraintsDisabler

# If set to true, all sequences and identity columns are set to a sufficiently high value, so that test data can be
# inserted without having manually chosen test record IDs clashing with automatically generated keys.
dbMaintainer.updateSequences.enabled=true
# Fully qualified classname of the implementation of org.unitils.dbmaintainer.sequences.SequenceUpdater
org.unitils.dbmaintainer.structure.SequenceUpdater.implClassName=org.unitils.dbmaintainer.structure.impl.DefaultSequenceUpdater
# Lowest acceptable value of a sequence in a unit test database. The SequenceUpdater will make sure all sequences
# have this value or higher before proceeding
sequenceUpdater.sequencevalue.lowestacceptable=1000

# Fully qualified classname of the implementation of org.unitils.dbmaintainer.clear.DBClearer
org.unitils.dbmaintainer.clean.DBClearer.implClassName=org.unitils.dbmaintainer.clean.impl.DefaultDBClearer
# Fully qualified classname of the implementation of org.unitils.dbmaintainer.clean.DBCleaner.
org.unitils.dbmaintainer.clean.DBCleaner.implClassName=org.unitils.dbmaintainer.clean.impl.DefaultDBCleaner

# Indicates whether the database should be cleaned before data updates are executed by the dbMaintainer. If true, the
# records of all database tables, except the ones listed in 'dbMaintainer.preserve.*' are deleted
dbMaintainer.cleanDb.enabled=true

# Comma separated list of database items that may not be dropped or cleared by the DB maintainer when
# updating the database from scratch (dbMaintainer.fromScratch.enabled=true).
# Schemas can also be preserved entirely. If identifiers are quoted (eg "" for oracle) they are considered
# case sensitive. Items that do not have a schema prefix are considered to be in the default schema, which
# is the first schema listed in the property database.schemaNames
dbMaintainer.preserve.schemas=
dbMaintainer.preserve.tables=
dbMaintainer.preserve.views=
dbMaintainer.preserve.materializedViews=
dbMaintainer.preserve.synonyms=
dbMaintainer.preserve.sequences=

# Comma separated list of tables that will not be emptied when the db maintainer performs a database
# update, if the property dbMaintainer.cleanDb.enabled is set to true.
# Tables listed here will still be dropped when the db maintainer performs a from scratch update. If this is not desirable
# you should add the tablename to the dbMaintainer.preserve.tables property instead
# Schemas can also be preserved entirely. If identifiers are quoted (eg "" for oracle) they are considered
# case sensitive. Items that do not have a schema prefix are considered to be in the default schema, which
# is the first schema listed in the property database.schemaNames
dbMaintainer.preserveDataOnly.schemas=
dbMaintainer.preserveDataOnly.tables=

# If set to true an XSD or DTD will be generated that represents the structure of the database schema. This XSD or DTD can be
# used in datafiles to verify if they are up-to-date and to enable code completion.
dbMaintainer.generateDataSetStructure.enabled=true
# Fully qualified name of the implementation of org.unitils.dbmaintainer.structure.DataSetStructureGenerator that is used.
# org.unitils.dbmaintainer.structure.impl.XsdDataSetStructureGenerator can be used to generate XSDs
# org.unitils.dbmaintainer.structure.impl.DtdDataSetStructureGenerator can be used to generate DTDs
org.unitils.dbmaintainer.structure.DataSetStructureGenerator.implClassName=org.unitils.dbmaintainer.structure.impl.XsdDataSetStructureGenerator
# DbUnit data set dtd file path
dataSetStructureGenerator.dtd.filename=
# DbUnit data set xsd file path
dataSetStructureGenerator.xsd.dirName=
# Suffix to use when generating complex types for tables
dataSetStructureGenerator.xsd.complexTypeSuffix=__type


# Fully qualified classname of the implementation of UnitilsTransactionManager that is used
org.unitils.database.transaction.UnitilsTransactionManager.implClassName=org.unitils.database.transaction.impl.DefaultUnitilsTransactionManager
# If set to true, the datasource injected onto test fields annotated with @TestDataSource or retrieved using
# DatabaseUnitils#getTransactionalDataSource are wrapped in a transactional proxy
dataSource.wrapInTransactionalProxy=true


# Default operation that is used for getting a dbunit dataset into the database. Should be the fully qualified classname
# of an implementation of org.unitils.dbunit.datasetloadstrategy.DataSetLoadStrategy
DbUnitModule.DataSet.loadStrategy.default=org.unitils.dbunit.datasetloadstrategy.impl.CleanInsertLoadStrategy
# Default factory that is used to create a dataset object from a file for the @DataSet annotation
DbUnitModule.DataSet.factory.default=org.unitils.dbunit.datasetfactory.impl.MultiSchemaXmlDataSetFactory
# Default factory that is used to create a dataset object from a file for the @ExpectedDataSet annotation
DbUnitModule.ExpectedDataSet.factory.default=org.unitils.dbunit.datasetfactory.impl.MultiSchemaXmlDataSetFactory

# Fully qualified classname of the data set resolver
org.unitils.dbunit.datasetfactory.DataSetResolver.implClassName=org.unitils.dbunit.datasetfactory.impl.DefaultDataSetResolver
# If set to true, the data set name will be prefixed with the package name of the test (with . replaced by /)
dbUnit.datasetresolver.prefixWithPackageName=true
# Optional prefix for the data set file name. If it starts with '/' it is treated as an absolute path on the
# file system, if not, it is treated as a classpath resource.
dbUnit.datasetresolver.pathPrefix=


# Fully qualified classnames of the different, dbms specific implementations of org.dbunit.dataset.datatype.IDataTypeFactory
org.dbunit.dataset.datatype.IDataTypeFactory.implClassName.oracle=org.dbunit.ext.oracle.OracleDataTypeFactory
org.dbunit.dataset.datatype.IDataTypeFactory.implClassName.oracle9=org.dbunit.ext.oracle.OracleDataTypeFactory
org.dbunit.dataset.datatype.IDataTypeFactory.implClassName.oracle10=org.dbunit.ext.oracle.OracleDataTypeFactory
org.dbunit.dataset.datatype.IDataTypeFactory.implClassName.db2=org.dbunit.ext.db2.Db2DataTypeFactory
org.dbunit.dataset.datatype.IDataTypeFactory.implClassName.hsqldb=org.dbunit.ext.hsqldb.HsqldbDataTypeFactory
org.dbunit.dataset.datatype.IDataTypeFactory.implClassName.mysql=org.dbunit.ext.mysql.MySqlDataTypeFactory
org.dbunit.dataset.datatype.IDataTypeFactory.implClassName.postgresql=org.dbunit.dataset.datatype.DefaultDataTypeFactory
org.dbunit.dataset.datatype.IDataTypeFactory.implClassName.derby=org.dbunit.dataset.datatype.DefaultDataTypeFactory
org.dbunit.dataset.datatype.IDataTypeFactory.implClassName.mssql=org.dbunit.ext.mssql.MsSqlDataTypeFactory


### DatabaseModule configuration ###
# Default behavior concerning execution of tests in a transaction. Supported values are 'disabled', 'commit' and 'rollback'.
# If set to disabled, test are not executed in a transaction by default. If set to commit, each test is run in a transaction,
# which is committed. If set to rollback, each test is run in a transaction, which is rolled back.
DatabaseModule.Transactional.value.default=commit

### MockModule configuration ###
mockModule.logFullScenarioReport=false
mockModule.logObservedScenario=false
mockModule.logDetailedObservedScenario=false
mockModule.logSuggestedAsserts=false

### EasyMockModule configuration ###
# Default value for order checking of method invocation on mocks. Supported values are 'none' and 'strict'
EasyMockModule.RegularMock.invocationOrder.default=none
# Default value for the calls property of mocks. Supported values are 'lenient' and 'strict'
EasyMockModule.RegularMock.calls.default=strict
# Default value for order checking of method invocation on mocks. Supported values are 'none' and 'strict'
EasyMockModule.Mock.invocationOrder.default=none
EasyMockModule.Mock.calls.default=strict
EasyMockModule.Mock.order.default=lenient
EasyMockModule.Mock.dates.default=strict
EasyMockModule.Mock.defaults.default=ignore_defaults
# Indicates whether after every test, the expected method calls are verified on all mock objects that were injected on
# fields annotated with @Mock or created with EasyMockUnitils.createMock (i.e. the verify() method is invoked on all
# these mocks.
EasyMockModule.autoVerifyAfterTest.enabled=true

### InjectModule configuration ###
# Mode of accessing properties
InjectModule.InjectIntoStatic.restore.default=old_value
InjectModule.InjectIntoByType.propertyAccess.default=field
InjectModule.InjectIntoStaticByType.restore.default=old_value
InjectModule.InjectIntoStaticByType.propertyAccess.default=field
InjectModule.TestedObject.createIfNull.enabled=true

### HibernateModule configuration ###
HibernateModule.configuration.implClassName=org.hibernate.cfg.AnnotationConfiguration

### JpaModule configuration ###
# Indicates the JPA persistence provider that is used. Supported values are 'hibernate', 'toplink' and 'openjpa'
jpa.persistenceProvider=hibernate

org.unitils.orm.jpa.util.JpaProviderSupport.implClassName.hibernate=org.unitils.orm.jpa.util.provider.hibernate.HibernateJpaProviderSupport
org.unitils.orm.jpa.util.JpaProviderSupport.implClassName.toplink=org.unitils.orm.jpa.util.provider.toplink.ToplinkJpaProviderSupport
org.unitils.orm.jpa.util.JpaProviderSupport.implClassName.openjpa=org.unitils.orm.jpa.util.provider.openjpa.OpenJpaProviderSupport

### SpringModule configuration ###
SpringModule.applicationContextFactory.implClassName=org.unitils.spring.util.ClassPathXmlApplicationContextFactory

spring.core.someClass.name=org.springframework.core.io.Resource


### IOModule configuration  ###
# The list of conversion strategies used.
# The propertiesConversionStrategy will convert a *.properties file to a properties object
# The StringConversationStrategy will to the same from a .txt file to a String
# It is possible to add you own strategies, or override these existing ones.
IOModule.conversion.default=org.unitils.io.conversion.impl.PropertiesConversionStrategy,org.unitils.io.conversion.impl.StringConversionStrategy
IOModule.conversion.custom=

IOModule.encoding.default=ISO-8859-1
# If set to true, the file name will be prefixed with the package name of the test (with . replaced by /)
IOModule.file.prefixWithPackageName=true
# Optional prefix for the file name. If it starts with '/' it is treated as an absolute path on the
# file system, if not, it is treated as a classpath resource.
IOModule.file.pathPrefix=

# If set to true, all temp files and directories created during a test will be deleted after the test.
IOModule.temp.cleanupAfterTest=false
# The root directory for all temp files and directories. If not specified, the default user temp dir will be used.
IOModule.temp.rootTempDir=


org.unitils.io.reader.FileResolvingStrategyFactory.implClassName=org.unitils.io.reader.impl.DefaultFileResolvingStrategyFactory
org.unitils.io.reader.ReadingStrategyFactory.implClassName=org.unitils.io.reader.impl.FileReadingStrategyFactory
org.unitils.io.filecontent.FileContentReaderFactory.implClassName=org.unitils.io.filecontent.impl.DefaultFileContentReaderFactory
org.unitils.io.temp.TempServiceFactory.implClassName=org.unitils.io.temp.impl.DefaultTempServiceFactory
2 楼 SunSteven 2013-12-17  
有几个问题,请帮忙解决
第一个问题:
@Test
@ExpectedDataSet("BaobaoTao.ExpectedBoards.xls")
public void addBoard()throws Exception {
//通过XlsDataSetBeanFactory数据集绑定工厂创建测试实体

List<Board> boards  = XlsDataSetBeanFactory.createBeans(BoardDaoTest.class,"BaobaoTao.SaveBoards.xls", "t_board", Board.class);
for(Board board:boards){
    boardDao.save(board);
}
}

因为要执行List<Board> boards  = XlsDataSetBeanFactory.createBeans(BoardDaoTest.class,"BaobaoTao.SaveBoards.xls", "t_board", Board.class);所以无法初始化t_board表。报这个错误。
看如何解决这个问题?

第二个问题:
  如果在db中已经有 t_board表,在实际执行的时候,如果执行异常(如表中数据比预计的多),db中任然有数据,看commit回滚等好像没有起到作用。另外,如果再次执行,数据也没有被清空,db中数据不断累积。难道这个框架在执行的时候,没有清理db的数据吗?

第三个问题:
  在这个类中,没有恢复将db清空或者删除数据表的动作,希望能进行补充。
1 楼 einstein618 2013-02-28  
用的是MySQL数据库,我的unitils-local.properties属性文件内容如下:
unitils.module.spring.className=org.unitils.spring.SpringModule
unitils.module.spring.runAfter=database
unitils.module.spring.enabled=true
database.userName=root
database.password=root
database.schemaNames=testdb
当执行测试时报错,请问是什么引起的?
Caused by: org.unitils.core.UnitilsException: Executed scripts table `testdb`.`dbmaintain_scripts` doesn't exist yet or is invalid.
Please create it manually or let Unitils create it automatically by setting the dbMaintainer.autoCreateExecutedScriptsTable property to true.
The table can be created manually by executing following statement:
create table `testdb`.`dbmaintain_scripts` ( file_name VARCHAR(150), version VARCHAR(25), file_last_modified_at BIGINT, checksum VARCHAR(50), executed_at VARCHAR(20), succeeded BIGINT )
...
org.unitils.dbmaintainer.version.impl.DefaultExecutedScriptInfoSource.checkExecu
at org.unitils.dbunit.DbUnitModule.getDefaultDbSupport(DbUnitModule.java:527)
at org.unitils.dbunit.DbUnitModule.getDataSetFactory(DbUnitModule.java:500)
at org.unitils.dbunit.DbUnitModule.getDataSetFactory(DbUnitModule.java:488)
at org.unitils.dbunit.DbUnitModule.getDataSet(DbUnitModule.java:312)
at org.unitils.dbunit.DbUnitModule.insertDataSet(DbUnitModule.java:147)
... 18 more

相关推荐

Global site tag (gtag.js) - Google Analytics