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;@Beforepublic void setUp() {ms = new MyService();}@Testpublic void test01() {//调用顺序不一致,不会报错s1 = createMock(IService1.class);s1.method2();expectLastCall();s1.method1();expectLastCall();replay(s1);ms.setS1(s1);ms.m1();verify(s1);}@Testpublic void test02() {s1 = createStrictMock(IService1.class);s1.method2();expectLastCall();s1.method1();expectLastCall();replay(s1);ms.setS1(s1);ms.m1();verify(s1);}@Testpublic 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的方式创建 */@Testpublic void test05() {//可以通过control来创建一组mockIMocksControl 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();}@Testpublic 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);}}