Junit学习笔记之四:dbunit和easymock

时间:2022-09-10 05:09:11

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 {

       //创建dbunitConnnection,需要传入一个数据库的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 {

       //创建dbunitConnnection,需要传入一个数据库的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、创建DAOMock对象,目前就进入了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);

    //创建ServiceDAO的关联

    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、创建DAOMock对象,目前就进入了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);

    //创建ServiceDAO的关联

    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、创建DAOMock对象,目前就进入了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);

    //创建ServiceDAO的关联

    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);}}