1.dbunit
用来隔离数据库的访问
测试流程大概是这样的,建立数据库连接-> 备份表 -> 调用Dao层接口 -> 从数据库取实际结果-> 事先准备的期望结果 -> 断言 -> 回滚数据库 -> 关闭数据库连接
1.1环境搭建
1、导入jar包
dbunit.jar
slf4j.jar
2、创建dbunit的测试数据xml文件
<?xmlversion="1.0"encoding="UTF-8"?> <dataset> <t_userid="1"username="admin"password="123"nickname="超级管理员"/> </dataset> |
3、创建dbunit的Connection
dbunit的Connection是用来对数据文件进行操作的,这个Connection必须依赖于目前项目中所使用的Connection
IDatabaseConnection con =new DatabaseConnection(DbUtil.getConnection()); |
4、创建IDataSet,通过DATASet来获取测试数据中的数据
/* * FlatXmlDataSet用来获取基于属性存储的属性值 * XMLDataSet用来获取基于节点类型存储的属性值 */ IDataSet ds = new FlatXmlDataSet( new FlatXmlProducer( new InputSource( TestDbUnit.class.getClassLoader().getResourceAsStream("t_user.xml")))); |
5、初始化数据并且完成测试
DatabaseOperation
定义了对数据库进行的操作,它是一个抽象类,通过静态字段提供了几种内置的实现:
- NONE:不执行任何操作,是
getTearDownOperation
的默认返回值。 - UPDATE:将数据集中的内容更新到数据库中。它假设数据库中已经有对应的记录,否则将失败。
- INSERT:将数据集中的内容插入到数据库中。它假设数据库中没有对应的记录,否则将失败。
- REFRESH:将数据集中的内容刷新到数据库中。如果数据库有对应的记录,则更新,没有则插入。
- DELETE:删除数据库中与数据集对应的记录。
- DELETE_ALL:删除表中所有的记录,如果没有对应的表,则不受影响。
- TRUNCATE_TABLE:与DELETE_ALL类似,更轻量级,不能rollback。
- CLEAN_INSERT:是一个组合操作,是DELETE_ALL和INSERT的组合。是
getSetUpOeration
的默认返回值。
//会将数据库中的数据清空,并且把测试数据插入 DatabaseOperation.CLEAN_INSERT.execute(con, ds); //从DAO中获取数据并且完成测试 IUserDao ud = new UserDao(); User tu = ud.load("admin"); assertEquals(tu.getId(), 1); assertEquals(tu.getUsername(),"admin"); assertEquals(tu.getPassword(),"123"); assertEquals(tu.getNickname(),"超级管理员"); |
1.1.2.还原数据库
1、备份
@Test public void testBackup() { try { //创建dbunit的Connnection,需要传入一个数据库的connection作为参数 IDatabaseConnection con = new DatabaseConnection(DbUtil.getConnection()); //根据con创建相应的dataset,这个dataset包含了所有的表 IDataSet ds = con.createDataSet(); //将ds中的数据通过FlatXmlDataSet的格式写到文件中 FlatXmlDataSet.write(ds,new FileWriter("d:/test.xml")); } catch (DataSetException e) { e.printStackTrace(); } catch (DatabaseUnitException e) { e.printStackTrace(); } catch (SQLException e) { //TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { //TODO Auto-generated catch block e.printStackTrace(); } |
以上演示的是备份数据库的所有文件 |
备份某些特定的表
public void testBackupTable() { try { //创建dbunit的Connnection,需要传入一个数据库的connection作为参数 IDatabaseConnection con = new DatabaseConnection(DbUtil.getConnection()); //通过QueryDataSet可以有效的选择要处理的表来作为数据集 QueryDataSet backup = new QueryDataSet(con); //添加t_user这张表作为备份表 backup.addTable("t_user"); FlatXmlDataSet.write(backup,new FileWriter("d:/test.xml")); |
2、还原数据库
代码:
package com.hust.junit;
import com.hust.junit.dao.IUserDao;
import com.hust.junit.dao.UserDao;
import com.hust.junit.model.User;
import com.hust.junit.util.DbUtil;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.dataset.xml.FlatXmlProducer;
import org.dbunit.operation.DatabaseOperation;
import org.junit.Test;
import org.xml.sax.InputSource;
import java.io.FileInputStream;
import java.io.FileWriter;
import static org.junit.Assert.assertEquals;
/**
* Created by Administration on 2016/6/5.
*/
public class DbUnitTest {
/**
* @throws Exception
*/
@Test
public void testLoad() throws Exception {
testBackupTalbe();
// 创建DbUnit的Connection,需要传入一个数据库的connection作为参数
IDatabaseConnection conn = new DatabaseConnection(DbUtil.openConnection());
// FlatXmlDataSet 用来获取基于属性存储的属性值
IDataSet ds = new FlatXmlDataSet(new FlatXmlProducer(new InputSource(DbUnitTest.class.getClassLoader().getResourceAsStream("t_user.xml"))));
// 会将数据库中的数据清空,并且把测试数据插入
DatabaseOperation.CLEAN_INSERT.execute(conn, ds);
// 从Dao中获取数据并且完成测试
IUserDao ud = new UserDao();
User tu = ud.load("admin");
assertEquals(tu.getUsername(), "admin");
assertEquals(tu.getPassword(), "admin");
assertEquals(tu.getNickname(), "超级管理员");
testResume();
}
@Test
public void testBackupAllTable() throws Exception {
// 创建DbUnit的Connection,需要传入一个数据库的connection作为参数
IDatabaseConnection conn = new DatabaseConnection(DbUtil.openConnection());
// 根据con创建相应的dataset,这个dataset包含了所有的表
IDataSet ds = conn.createDataSet();
FlatXmlDataSet.write(ds, new FileWriter("d:\\test.xml"));
}
@Test
public void testBackupTalbe() throws Exception {
// 创建DbUnit的Connection,需要传入一个数据库的connection作为参数
IDatabaseConnection conn = new DatabaseConnection(DbUtil.openConnection());
// 根据con创建相应的dataset,通过QueryDataSet可以有效的选择要处理的表来作为数据集
QueryDataSet qds = new QueryDataSet(conn);
//添加t_user这张表作为备份表
qds.addTable("t_user");
FlatXmlDataSet.write(qds, new FileWriter("d:\\test.xml"));
}
@Test
public void testResume() throws Exception {
// 创建DbUnit的Connection,需要传入一个数据库的connection作为参数
IDatabaseConnection conn = new DatabaseConnection(DbUtil.openConnection());
// FlatXmlDataSet 用来获取基于属性存储的属性值
IDataSet ds = new FlatXmlDataSet(new FlatXmlProducer(new InputSource(new FileInputStream("d:\\test.xml"))));
// 会将数据库中的数据清空,并且把测试数据插入
DatabaseOperation.CLEAN_INSERT.execute(conn, ds);
}
}
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DbUtil {
public static Connection getConnection() throws SQLException {
Connection con = null;
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/junit", "root", "123456");
return con;
}
public static void close(Connection con) {
try {
if(con!=null) con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(PreparedStatement ps) {
try {
if(ps!=null) ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(ResultSet rs) {
try {
if(rs!=null) rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
创建数据库junit,并创建表t_user
CREATE TABLE if NOT EXISTS t_user(
id int(10) auto_increment,
username VARCHAR(200),
password VARCHAR(200),
nickname VARCHAR(200),
constraint pk_user primary key(id)
)charset=utf8 ENGINE=InnoDB;
2.easymock
mock对象用来对一些未实现关联对象的类进行测试的对象
mock和stub的区别
mock关注的是交互
stub关注的是状态
EasyMock就是实现Mock对象的框架
2.1运行环境
1、导入easymock的jar包
2、Mock对象的生命周期
三个阶段:record,replay,verify
Mock的关注点是在交互上,主要解决的问题是对象之间的交互,诸如:Service就依赖于DAO,如果DAO没有实现,我们可以通过Mock来模拟DAO的实现。
record阶段就是用来说明这个DAO上面可能存在的值
@Test publicvoid testLoad() { //1、创建DAO的Mock对象,目前就进入了record阶段 IUserDao ud = EasyMock.createMock(IUserDao.class); User u = new User(1,"admin","123","管理员"); //2、记录ud可能会发生的操作的结果 /* * 以下代码所指的是,当在dao中调用了load方法并且参数为admin的时候,返回值是u对象 */ //必须把交互的所有过程都记录下来 EasyMock.expect(ud.load("asd")).andReturn(u); ud.delete("abc"); //以下用来操作没有返回值的方法 EasyMock.expectLastCall(); EasyMock.expect(ud.load("admin")).andReturn(u); //3、进入测试阶段,也就是replay阶段 EasyMock.replay(ud); //创建Service和DAO的关联 IUserService us = new UserService(ud); //完成测试 User tu = us.load("admin"); EntitiesHelper.assertUser(tu,u); //3、验证交互关系是否正确 EasyMock.verify(ud); } |
2.2Mock的几种创建方式
1、createMock
通过createMock说创建的mock对象,在进行verify的时候仅仅只是检查关联方法是否正常完成调用,如果完成次数一致就认为测试通过,不考虑顺序问题
@Test publicvoid testLoadMock() { //1、创建DAO的Mock对象,目前就进入了record阶段 IUserDao ud = EasyMock.createMock(IUserDao.class); User u = new User(1,"admin","123","管理员"); EasyMock.expect(ud.load("asd")).andReturn(u); //使用的createMock,如果方法的调用顺序不一致,不会抛出异常 ud.delete("abc"); EasyMock.expectLastCall(); EasyMock.expect(ud.load("admin")).andReturn(u); EasyMock.replay(ud); //创建Service和DAO的关联 IUserService us = new UserService(ud); //完成测试 User tu = us.load("admin"); EntitiesHelper.assertUser(tu,u); //3、验证交互关系是否正确 EasyMock.verify(ud); } |
2、createStrictMock
在verify时不仅仅验证关联方法的调用次数还要验证顺序
@Test publicvoid testLoadStrictMock() { //1、创建DAO的Mock对象,目前就进入了record阶段 IUserDao ud = EasyMock.createStrictMock(IUserDao.class); User u = new User(1,"admin","123","管理员"); EasyMock.expect(ud.load("asd")).andReturn(u); //使用的createStrictMock,方法的顺序不一致,所以会抛出异常 ud.delete("abc"); EasyMock.expectLastCall(); EasyMock.expect(ud.load("admin")).andReturn(u); EasyMock.replay(ud); //创建Service和DAO的关联 IUserService us = new UserService(ud); //完成测试 User tu = us.load("admin"); EntitiesHelper.assertUser(tu,u); //3、验证交互关系是否正确 EasyMock.verify(ud); } |
3、基于MockCongrol的创建
/** * 使用mockControl可以检查一组调用对象之间的关系 * 所以如果希望使用Strict的方式,而且依赖了两个类以上,这两个依赖类应该通过control的方式创建 */ @Test publicvoid test05() { //可以通过control来创建一组mock IMocksControl mc = createStrictControl(); s1 = mc.createMock(IService1.class); s2 = mc.createMock(IService2.class); s1.method1(); expectLastCall(); s1.method2(); expectLastCall(); s2.method3(); expectLastCall(); s2.method4(); expectLastCall(); //让mock控制器中的进行操作 mc.replay(); ms.setS1(s1); ms.setS2(s2); ms.m2(); //验证mock控制器中的所有mock调用 mc.verify(); } |
代码:
package com.hust.junit.service;
/**
* Created by Administration on 2016/6/6.
*/
public interface IService1 {
void method1();
void method2();
}
package com.hust.junit.service;
/**
* Created by Administration on 2016/6/6.
*/
public interface IService2 {
void method3();
void method4();
}
package com.hust.junit.service;
/**
* Created by Administration on 2016/6/6.
*/
public class MyService {
private IService1 service1;
private IService2 service2;
public IService1 getService1() {
return service1;
}
public void setService1(IService1 service1) {
this.service1 = service1;
}
public IService2 getService2() {
return service2;
}
public void setService2(IService2 service2) {
this.service2 = service2;
}
public void m1() {
service1.method1();
service1.method2();
}
public void m2() {
service1.method1();
service2.method3();
service1.method2();
service2.method4();
}
}
package com.hust.junit;
import org.easymock.IMocksControl;
import org.junit.Before;
import org.junit.Test;
import com.hust.junit.IService1;
import com.hust.junit.IService2;
import com.hust.junit.MyService;
import static org.easymock.EasyMock.*;
public class TestStrictMock {
private IService1 s1;
private IService2 s2;
private MyService ms;
@Before
public void setUp() {
ms = new MyService();
}
@Test
public void test01() {
//调用顺序不一致,不会报错
s1 = createMock(IService1.class);
s1.method2();
expectLastCall();
s1.method1();
expectLastCall();
replay(s1);
ms.setS1(s1);
ms.m1();
verify(s1);
}
@Test
public void test02() {
s1 = createStrictMock(IService1.class);
s1.method2();
expectLastCall();
s1.method1();
expectLastCall();
replay(s1);
ms.setS1(s1);
ms.m1();
verify(s1);
}
@Test
public void test03() {
/**
* 此时只会分别来判断s1和s2的顺序,如果两个接口中的方法调用顺序一致
* 就不会报错,而具体的交互的顺序的确不一致
*/
s1 = createStrictMock(IService1.class);
s2 = createStrictMock(IService2.class);
s1.method1();
expectLastCall();
s1.method2();
expectLastCall();
s2.method3();
expectLastCall();
s2.method4();
expectLastCall();
replay(s1,s2);
ms.setS1(s1);
ms.setS2(s2);
ms.m2();
verify(s1,s2);
}
/**
* 使用mockControl可以检查一组调用对象之间的关系
* 所以如果希望使用Strict的方式,而且依赖了两个类以上,这两个依赖类应该通过control的方式创建
*/
@Test
public void test05() {
//可以通过control来创建一组mock
IMocksControl mc = createStrictControl();
s1 = mc.createMock(IService1.class);
s2 = mc.createMock(IService2.class);
s1.method1();
expectLastCall();
s1.method2();
expectLastCall();
s2.method3();
expectLastCall();
s2.method4();
expectLastCall();
//让mock控制器中的进行操作
mc.replay();
ms.setS1(s1);
ms.setS2(s2);
ms.m2();
//验证mock控制器中的所有mock调用
mc.verify();
}
@Test
public void test04() {
/**
*createMock不管怎么放顺序都不会报错
*/
s1 = createMock(IService1.class);
s2 = createMock(IService2.class);
s1.method2();
expectLastCall();
s2.method3();
expectLastCall();
s2.method4();
expectLastCall();
s1.method1();
expectLastCall();
replay(s1,s2);
ms.setS1(s1);
ms.setS2(s2);
ms.m2();
verify(s1,s2);
}
}