DbUnit入门实战
原文地址: http://yangzb.iteye.com/blog/947292
相信做过单元测试的人都会对JUnit 非常的熟悉了, 今天要介绍的DbUnit(http://dbunit.sourceforge.net/ ) 则是专门针对数据库测试的对JUnit 的一个扩展, 它可以将测试对象数据库置于一个测试轮回之间的状态. 鉴于目前国内介绍DbUnit 的系统教程比较少见,本文将分从理论和实例两个方面带你领略DbUnit 的精彩世界。
DbUnit 设计理念
熟悉单元测试的开发人员都知道,在对数据库进行单元测试时候,通常采用的方案有运用模拟对象(mock objects)和stubs 两种。通过隔离关联的数据库访问类,比如JDBC 的相关操作类,来达到对数据库操作的模拟测试。然而某些特殊的系统,比如利用了EJB 的CMP(container-managed persistence) 的系统,数据库的访问对象是在最底层而且很隐蔽的,那么这两种解决方案对这些系统就显得力不从心了。
DBUnit 的设计理念就是在测试之前,备份数据库,然后给对象数据库植入我们需要的准备数据,最后,在测试完毕后,读入备份数据库,回溯到测试前的状态;
而且又因为DBUnit 是对JUnit 的一种扩展,开发人员可以通过创建测试用例代码,在这些测试用例的生命周期内来对数据库的操作结果进行比较。
DbUnit 测试基本概念和流程
基于DbUnit 的测试的主要接口是IDataSet 。IDataSet 代表一个或多个表的数据。
可以将数据库模式的全部内容表示为单个IDataSet 实例。这些表本身由Itable 实例来表示。
IDataSet 的实现有很多,每一个都对应一个不同的数据源或加载机制。最常用的几种 IDataSet 实现为:
FlatXmlDataSet:数据的简单平面文件 XML 表示
QueryDataSet:用 SQL 查询获得的数据
DatabaseDataSet:数据库表本身内容的一种表示
XlsDataSet:数据的excel 表示
一般而言,使用DbUnit 进行单元测试的流程如下:
1. 根据业务,做好测试用的准备数据和预想结果数据,通常准备成xml 格式文件。
2. 在setUp() 方法里边备份数据库中的关联表。
3. 在setUp() 方法里边读入准备数据。
4. 对测试类的对应测试方法进行实装: 执行对象方法,把数据库的实际执行结果和预想结果进行比较。
5. 在tearDown() 方法里边, 把数据库还原到测试前状态。
DbUnit 开发实例
下面通过一个实例来说明DbUnit 的实际运用。
实例准备
比如有一个学生表[student] ,结构如下:
--------------------------------------------------------------------------------
id char(4) pk 学号
name char(50) 姓名
sex char(1) 性别
birthday date 出生日期
--------------------------------------------------------------------------------
准备数据如下:
--------------------------------------------------------------------------------
id name sex birthday
0001 翁仔 m 1979-12-31
0002 王翠花 f 1982-08-09
--------------------------------------------------------------------------------
测试对象类为StudentOpe.java ,里边有2 个方法:
findStudent(String id) : 根据主键id 找记录
addStudent(Student student) :添加一条记录
在测试addStudent 方法时候,我们准备添加如下一条数据
--------------------------------------------------------------------------------
id name sex birthday
0088 王耳朵 m 1982-01-01
--------------------------------------------------------------------------------
那么在执行该方法后,数据库的student 表里的数据是这样的:
--------------------------------------------------------------------------------
id name sex birthday
0001 翁仔 m 1979-12-31
0002 王翠花 f 1982-08-09
0088 王耳朵 m 1982-01-01
--------------------------------------------------------------------------------
然后我们说明如何对这2 个方法进行单元测试。
实例展开
1 把准备数据和预想数据转换成xml 文件
student_pre.xml
--------------------------------------------------------------------------------
<?xml version='1.0' encoding="gb2312"?>
<dataset>
<student id="0001" name=" 翁仔" sex="m" birthday="1979-12-31"/>
<student id="0002" name=" 王翠花" sex="f" birthday="1982-08-09"/>
</dataset>
--------------------------------------------------------------------------------
student_exp.xml
--------------------------------------------------------------------------------
<?xml version='1.0' encoding="gb2312"?>
<dataset>
<student id="0001" name=" 翁仔" sex="m" birthday="1979-12-31"/>
<student id="0002" name=" 王翠花" sex="f" birthday="1982-08-09"/>
<student id="0088" name=" 王耳朵" sex="m" birthday="1982-01-01"/>
</dataset>
--------------------------------------------------------------------------------
2 实装setUp 方法,详细见代码注释。
--------------------------------------------------------------------------------
protected void setUp() {
IDatabaseConnection connection = null;
try {
super.setUp();
// 本例使用postgresql 数据库
Class.forName("org.postgresql.Driver");
// 连接DB
Connection conn = DriverManager.getConnection(
"jdbc:postgresql:testdb.test", "postgres", "postgres");
// 获得DB 连接
connection = new DatabaseConnection(conn);
// 对数据库中的操作对象表student 进行备份
QueryDataSet backupDataSet = new QueryDataSet(connection);
backupDataSet.addTable("student");
file = File.createTempFile("student_back", ".xml");// 备份文件
FlatXmlDataSet.write(backupDataSet, new FileOutputStream(file));
// 准备数据的读入
IDataSet dataSet = new FlatXmlDataSet(new FileInputStream(
"student_pre.xml"));
DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (connection != null)
connection.close();
} catch (SQLException e) {
}
}
}
--------------------------------------------------------------------------------
3 实装测试方法,详细见代码注释。
* 检索类方法,可以利用assertEquals() 方法,拿表的字段进行比较。
--------------------------------------------------------------------------------
// findStudent
public void testFindStudent() throws Exception {
// 执行findStudent 方法
StudentOpe studentOpe = new StudentOpe();
Student result = studentOpe.findStudent("0001"); // 预想结果和实际结果的比较
assertEquals(" 翁仔", result.getName());
assertEquals("m", result.getSex());
assertEquals("1979-12-31", result.getBirthDay());
}
--------------------------------------------------------------------------------
* 更新,添加,删除等方法,可以利用Assertion.assertEquals() 方法,拿表的整体来比较。
--------------------------------------------------------------------------------
public void testAddStudent() throws Exception {
// 执行addStudent 方法
StudentOpe studentOpe = new StudentOpe();
// 被追加的记录
Student newStudent = new Student("0088", " 王耳朵", "m", "1982-01-01");
// 执行追加方法
Student result = studentOpe.addStudent(newStudent);
// 预想结果和实际结果的比较
IDatabaseConnection connection = null;
try {
// 预期结果取得
IDataSet expectedDataSet = new FlatXmlDataSet(new FileInputStream(
"student_exp.xml"));
ITable expectedTable = expectedDataSet.getTable("student");
// 实际结果取得
Connection conn = getConnection();
connection = new DatabaseConnection(conn);
IDataSet databaseDataSet = connection.createDataSet();
ITable actualTable = databaseDataSet.getTable("student");
// 比较
Assertion.assertEquals(expectedTable, actualTable);
} finally {
if (connection != null)
connection.close();
}
}
--------------------------------------------------------------------------------
* 如果在整体比较表的时候,有个别字段不需要比较,可以用DefaultColumnFilter.excludedColumnsTable() 方法,
将指定字段给排除在比较范围之外。比如上例中不需要比较birthday 这个字段的话,那么可以如下代码所示进行处理:
--------------------------------------------------------------------------------
ITable filteredExpectedTable = DefaultColumnFilter.excludedColumnsTable(expectedTable, new String[]{"birthday"});
ITable filteredActualTable = DefaultColumnFilter.excludedColumnsTable(actualTable,new String[]{"birthday"});
Assertion.assertEquals(filteredExpectedTable, filteredActualTable);
--------------------------------------------------------------------------------
4 在tearDown() 方法里边, 把数据库还原到测试前状态
--------------------------------------------------------------------------------
protected void tearDown() throws Exception {
IDatabaseConnection connection = null;
try {
super.tearDown();
Connection conn = getConnection();
connection = new DatabaseConnection(conn);
IDataSet dataSet = new FlatXmlDataSet(file);
DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (connection != null)
connection.close();
} catch (SQLException e) {
}
}
}
<property name="dbUrl" value="jdbc:oracle:thin:@192.168.104.47:1521:esample" />
<property name="dbUser" value="esample" />
<property name="dbPassword" value="esample" />
<property name="operation" value="CLEAN_INSERT" />
<property name="file" value="partial.xml" />
<dbunit driver="${dbDriver}" url="${dbUrl}" userid="${dbUser}" password="${dbPassword}">
<operation type="${operation}" src="${file}" format="xml" />
</dbunit>
</target>
<dbunit driver="${dbDriver}" url="${dbUrl}" userid="${dbUser}" password="${dbPassword}">
<export dest="partial.xml" format="xml">
<query name="QueryExhibtion" sql="SELECT Exhibition_Id FROM Ex_exhibition " />
<table name="ex_exhibition" />
</export>
</dbunit>
</target>
</project>
DbUnit入门实战的更多相关文章
- Spark入门实战系列--10.分布式内存文件系统Tachyon介绍及安装部署
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .Tachyon介绍 1.1 Tachyon简介 随着实时计算的需求日益增多,分布式内存计算 ...
- 003-Tuple、Array、Map与文件操作入门实战
003-Tuple.Array.Map与文件操作入门实战 Tuple 各个元素可以类型不同 注意索引的方式 下标从1开始 灵活 Array 注意for循环的until用法 数组的索引方式 上面的for ...
- Scala深入浅出实战经典-----002Scala函数定义、流程控制、异常处理入门实战
002-Scala函数定义.流程控制.异常处理入门实战 Scala函数定义 语句结束无分号 定义无参函数 def 函数名称(参数名称:参数类型)[:Unit=]{ 函数体 } 老师的代码 我的实际代码 ...
- Spark入门实战系列--1.Spark及其生态圈简介
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .简介 1.1 Spark简介 年6月进入Apache成为孵化项目,8个月后成为Apache ...
- Spark入门实战系列--2.Spark编译与部署(上)--基础环境搭建
[注] 1.该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取: 2.Spark编译与部署将以CentOS 64位操作系统为基础,主要是考虑到实际应用 ...
- Spark入门实战系列--2.Spark编译与部署(中)--Hadoop编译安装
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .编译Hadooop 1.1 搭建环境 1.1.1 安装并设置maven 1. 下载mave ...
- Spark入门实战系列--2.Spark编译与部署(下)--Spark编译安装
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .编译Spark .时间不一样,SBT是白天编译,Maven是深夜进行的,获取依赖包速度不同 ...
- Spark入门实战系列--3.Spark编程模型(上)--编程模型及SparkShell实战
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .Spark编程模型 1.1 术语定义 l应用程序(Application): 基于Spar ...
- Spark入门实战系列--3.Spark编程模型(下)--IDEA搭建及实战
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 . 安装IntelliJ IDEA IDEA 全称 IntelliJ IDEA,是java语 ...
随机推荐
- mysql在Linux下大小写敏感设置
默认情况下,mysql在windows下是不区分大小写的,但是mysql在linux下大小写规则是这样的: 1.数据库名与表名是严格区分大小写的: 2.表的别名是严格区分大小写的: 3.列名与列的别名 ...
- Redis_安装配置(2)
一.安装gcc依赖 由于 redis 是用 C 语言开发,安装之前必先确认是否安装 gcc 环境(gcc -v),如果没有安装,执行以下命令进行安装 $ yum install -y gcc 上面命令 ...
- LC 二叉树的最大深度
https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/xnd69e/ Recursion /** * Definitio ...
- 为 MySQL 的 root 用户设置一个密码。
shell> mysqladmin --user=root password somepasswordshell> mysqladmin --user=root --password re ...
- Elasticsearch基础知识学习
概要 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Ap ...
- FastDFSJava客户端使用
1.1.java客户端 余庆先生提供了一个Java客户端,但是作为一个C程序员,写的java代码可想而知.而且已经很久不维护了. 这里推荐一个开源的FastDFS客户端,支持最新的SpringBoot ...
- SSRF打认证的redis
redis客户端在向服务端传输数据用到的是RESP协议 客户端向Redis服务器发送一个仅由Bulk Strings组成的RESP Arrays. Redis服务器回复发送任何有效RESP数据类型作为 ...
- markdown mermaid状态图
状态图 状态图是一种用于计算机科学和相关领域描述系统行为的图.状态图要求描述的系统由有限数量的状态组成. 语法: stateDiagram-v2 [*] --> Still Still --&g ...
- C\C++ IDE 比较以及调试
C\C++ IDE 比较以及调试 内容概要 这个作业属于哪个课程 2022面向对象程序设计 这个作业要求在哪里 2022面向对象程序设计寒假作业1 这个作业的目标 IDE 选择以及代码调试 作业正文 ...
- 毫米转像素dpi
public static double MillimeterToPixel_X(double length) //length是毫米,1厘米=10毫米 { System.Windows.Forms. ...