当前位置: 移动技术网 > IT编程>开发语言>Java > 对dbunit进行mybatis DAO层Excel单元测试(必看篇)

对dbunit进行mybatis DAO层Excel单元测试(必看篇)

2019年07月22日  | 移动技术网IT编程  | 我要评论

dao层测试难点

可重复性,每次运行单元测试,得到的数据是重复的

独立性,测试数据与实际数据相互独立

数据库中脏数据预处理

不能给数据库中数据带来变化

dao层测试方法

使用内存数据库,如h2。优点:无需清空无关数据;缺点:单元测试中需要进行数据库初始化过程,如果初始化过程复杂,单元测试工作量增大

使用dbunit。优点:数据库初始化简单,大大减轻单元测试工作量;缺点:目前官方提供jar包只支持xml格式文件,需要自己开发excel格式文件

基于dbunit进行dao单元测试

应用环境:spring、mybatis、mysql、excel

配置文件

1. pom.xml

引入jar包,unitils整合了dbunit,database,spring,io等模块

<dependency>
    <groupid>org.unitils</groupid>
    <artifactid>unitils-core</artifactid>
    <version>3.4.2</version>
  </dependency>
  <dependency>
    <groupid>org.unitils</groupid>
    <artifactid>unitils-dbunit</artifactid>
    <version>3.4.2</version>
  </dependency>
  <dependency>
    <groupid>org.unitils</groupid>
    <artifactid>unitils-io</artifactid>
    <version>3.4.2</version>
  </dependency>
  <dependency>
    <groupid>org.unitils</groupid>
    <artifactid>unitils-database</artifactid>
    <version>3.4.2</version>
  </dependency>
  <dependency>
    <groupid>org.unitils</groupid>
    <artifactid>unitils-spring</artifactid>
    <version>3.4.2</version>
  </dependency>
  <dependency>
    <groupid>org.dbunit</groupid>
    <artifactid>dbunit</artifactid>
    <version>2.5.3</version>
  </dependency>

配置maven对resourcew文件过滤规则,如果不过滤maven会对resource文件重编码,导致excel文件被破坏

<resources>
    <resource>
      <directory>src/test/resources</directory>
      <includes>
        <include>**/*.*</include>
      </includes>
      <filtering>false</filtering>
    </resource>
  </resources>

2. unitils.properties

在测试源码根目录中创建一个项目级别的unitils.properties配置文件,主要用于配置自定义拓展模块,数据加载等相关信息

#启用unitils所需模块
unitils.modules=database,dbunit

#自定义扩展模块,加载excel文件,默认拓展模块org.unitils.dbunit.dbunitmodule支持xml
unitils.module.dbunit.classname=org.agoura.myunit.module.mydbunitmodule

#配置数据库连接
database.driverclassname=com.mysql.jdbc.driver
database.url=jdbc:mysql://127.0.0.1:3306/teams?autoreconnect=true&useunicode=true&characterencoding=utf-8
database.username=root
database.password=agoura
#配置为数据库名称
database.schemanames=teams
#配置数据库方言
database.dialect=mysql

#需设置false,否则我们的测试函数只有在执行完函数体后,才将数据插入的数据表中
unitils.module.database.runafter=false

#配置数据库维护策略.请注意下面这段描述
# 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=true

#配置数据库表创建策略,是否自动建表以及建表sql脚本存放目录
dbmaintainer.autocreateexecutedscriptstable=true
dbmaintainer.keepretryingaftererror.enabled=true
dbmaintainer.script.locations=src/main/resources/dbscripts
#dbmaintainer.script.fileextensions=sql

#数据集加载策略
#cleaninsertloadstrategy:先删除dateset中有关表的数据,然后再插入数据
#insertloadstrategy:只插入数据
#refreshloadstrategy:有同样key的数据更新,没有的插入
#updateloadstrategy:有同样key的数据更新,没有的不做任何操作
dbunitmodule.dataset.loadstrategy.default=org.unitils.dbunit.datasetloadstrategy.impl.cleaninsertloadstrategy

#配置数据集工厂,自定义
dbunitmodule.dataset.factory.default=org.agoura.myunit.utils.multischemaxlsdatasetfactory
dbunitmodule.expecteddataset.factory.default=org.agoura.myunit.utils.multischemaxlsdatasetfactory

#配置事务策略 commit、rollback 和disabled;或者在代码的方法上标记@transactional(value=transactionmode.rollback)
#commit 是单元测试方法过后提交事务
#rollback 是回滚事务
#disabled 是没有事务,默认情况下,事务管理是disabled
databasemodule.transactional.value.default=commit

#配置数据集结构模式xsd生成路径,可以自定义目录,但不能为空
datasetstructuregenerator.xsd.dirname=src/main/resources/xsd
dbmaintainer.generatedatasetstructure.enabled=true

#文件相对路径是否是测试类文件路径,false表示resource根目录
dbunit.datasetresolver.prefixwithpackagename=false

3. spring-mybatis-unitils.xml

<?xml version="1.0" encoding="gbk"?>
<beans xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemalocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.1.xsd">

  <context:component-scan base-package="com.agoura.agoura"/>

  <context:property-placeholder location="classpath:jdbc_dbcp.properties"/>
  <!--<util:properties id="jdbc_dbcp" />-->

  <bean id="datasource" class="org.unitils.database.unitilsdatasourcefactorybean"/>

  <!-- spring和mybatis整合,不需要mybatis的配置映射文件 -->
  <bean id="sqlsessionfactory" class="org.mybatis.spring.sqlsessionfactorybean">
    <property name="datasource" ref="datasource"/>
    <!-- 自动扫描mapping.xml文件 -->
    <property name="mapperlocations" value="classpath*:com/agoura/agoura/mapper/xml/*.xml"></property>
  </bean>

  <!-- dao接口所在包名,spring会自动查找其下的类 -->
  <bean class="org.mybatis.spring.mapper.mapperscannerconfigurer">
    <property name="basepackage" value="com.agoura.agoura.mapper"/>
    <property name="sqlsessionfactorybeanname" value="sqlsessionfactory"></property>
  </bean>

  <!-- (事务管理)transaction manager, use jtatransactionmanager for global tx -->
  <bean id="transactionmanager"
     class="org.springframework.jdbc.datasource.datasourcetransactionmanager">
    <property name="datasource" ref="datasource"/>
  </bean>
</beans>

dbunit执行流程

dbunit通过@dataset注解读取模拟数据excel文件,流程如下:

excel文件 --> @dataset --> dbunitmodule --> datasetfactory --> 数据库(mysql)

@dataset:将指定路径下excel文件加载到dbunitmodule中

dbunitmodule:对传入文件进行预处理,源代码中对传入的xml文件copy一份临时文件,并将临时文件交给datasetfactory处理,处理完后再删除临时文件

datasetfactory:将读取的excel数据转换为multischemadataset,准备放入数据库中

由于原代码dbunitmodule中只有对xml文件的预处理,而我们是要对excel文件进行预处理,所以需要对dbunitmodule进行重写。重写内容为:完善dbunitdatabaseconnection连接;针对excel文件,修改预处理实现;修改文件处理后续操作。示例如下:

import org.dbunit.database.databaseconfig;
import org.dbunit.ext.mysql.mysqldatatypefactory;
import org.dbunit.ext.mysql.mysqlmetadatahandler;
import org.unitils.core.unitilsexception;
import org.unitils.dbmaintainer.locator.classpathdatalocator;
import org.unitils.dbmaintainer.locator.resourcepickingstrategie.resourcepickingstrategie;
import org.unitils.dbunit.dbunitmodule;
import org.unitils.dbunit.datasetfactory.datasetfactory;
import org.unitils.dbunit.util.dbunitdatabaseconnection;
import org.unitils.dbunit.util.multischemadataset;

import java.io.file;
import java.io.inputstream;
import java.util.arraylist;
import java.util.list;

public class mydbunitmodule extends dbunitmodule {

  //完善dbunitdatabaseconnection连接信息
  @override
  public dbunitdatabaseconnection getdbunitdatabaseconnection(final string schemaname) {
    dbunitdatabaseconnection result = dbunitdatabaseconnections.get(schemaname);
    if (result != null) {
      return result;
    }

    result = super.getdbunitdatabaseconnection(schemaname);

    result.getconfig().setproperty(databaseconfig.property_datatype_factory, new mysqldatatypefactory());
    result.getconfig().setproperty(databaseconfig.property_metadata_handler, new mysqlmetadatahandler());
    return result;
  }

  //excel预处理操作,将@dataset注释读取的文件返回给datasetfactory进行处理
  @override
  protected file handledatasetresource(classpathdatalocator locator, string nameresource, resourcepickingstrategie strategy, class<?> testclass) {
    string cloneresource = new string(nameresource);
    string packagename = testclass.getpackage() != null?testclass.getpackage().getname():"";
    string tempname = "";
    if(cloneresource.startswith(packagename.replace(".", "/"))) {
      cloneresource = tempname = cloneresource.substring(packagename.length());
    } else if(cloneresource.startswith(packagename)) {
      cloneresource = tempname = cloneresource.substring(packagename.length() + 1);
    } else {
      tempname = cloneresource;
    }

    inputstream in = locator.getdataresource(packagename.replace(".", "/") + "/" + tempname, strategy);
    file resolvedfile = null;
    if(in == null) {
      resolvedfile = this.getdatasetresolver().resolve(testclass, cloneresource);
      if(resolvedfile == null) {
        throw new unitilsexception("datasetresource file with name '" + nameresource + "' cannot be found");
      }
    }

    return resolvedfile;
  }

  //调用datasetfactory.createdataset()向数据库中注入excel数据后,直接返回dataset,不对dataset执行清零操作
  @override
  protected multischemadataset getdataset(class<?> testclass, string[] datasetfilenames, datasetfactory datasetfactory) {
    list<file> datasetfiles = new arraylist<file>();

    resourcepickingstrategie resourcepickingstrategie = getresourcepickingstrategie();

    for (string datasetfilename : datasetfilenames) {
      file datasetfile = handledatasetresource(new classpathdatalocator(), datasetfilename, resourcepickingstrategie, testclass);
      datasetfiles.add(datasetfile);
    }

    multischemadataset dataset = datasetfactory.createdataset(datasetfiles.toarray(new file[datasetfiles.size()]));
    return dataset;
  }
}

拓展模块dbunitmodule重写完后,由于官方版本中datasetfactory只对xml文件进行处理,为了能处理excel文件,需要对datasetfactory进行重写。示例如下:

import org.unitils.core.unitilsexception;
import org.unitils.dbunit.datasetfactory.datasetfactory;
import org.unitils.dbunit.util.multischemadataset;

import java.io.file;
import java.util.*;

public class multischemaxlsdatasetfactory implements datasetfactory {
  protected string defaultschemaname;

  public void init(properties configuration, string s) {
    this.defaultschemaname = s;
  }

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

createdataset()为自定义的数据集工厂multischemaxlsdatasetfactory中的核心方法,主要是读取传入的excel文件,将读取数据写入mutischemaxlsdataset中。multischemaxlsdatasetreader通过poi实现了读取excel数据功能,可以同时读取多个数据集,也即多个模拟数据库数据。

import org.dbunit.database.ambiguoustablenameexception;
import org.dbunit.dataset.defaultdataset;
import org.dbunit.dataset.idataset;
import org.dbunit.dataset.itable;
import org.dbunit.dataset.excel.xlsdataset;
import org.unitils.core.unitilsexception;
import org.unitils.dbunit.util.multischemadataset;

import java.io.file;
import java.io.fileinputstream;
import java.util.*;

public class multischemaxlsdatasetreader {
  private string pattern = ".";
  private string defaultschemaname;

  public multischemaxlsdatasetreader(string defaultschemaname) {
    this.defaultschemaname = defaultschemaname;
  }

  public multischemadataset readdatasetxls(file... datasetfiles) {
    try {
      map<string, list<itable>> tbmap = gettables(datasetfiles);
      multischemadataset datasets = new multischemadataset();

      for (map.entry<string, list<itable>> entry : tbmap.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);
    }
  }

  private map<string, list<itable>> gettables(file... datasetfiles) {
    map<string, list<itable>> tablemap = new hashmap<>();
    // 需要根据schema把table重新组合一下
    try {
      string schema, tablename;
      for (file file : datasetfiles) {
        idataset dataset = new xlsdataset(new fileinputstream(file));
        string[] tablenames = dataset.gettablenames();
        for (string tn : tablenames) {
          string[] temp = tn.split(pattern);
          if (temp.length == 2) {
            schema = temp[0];
            tablename = temp[1];
          } else {
            schema = this.defaultschemaname;
            tablename = tn;
          }

          itable table = dataset.gettable(tn);
          if (!tablemap.containskey(schema)) {
            tablemap.put(schema, new arraylist<itable>());
          }
          tablemap.get(schema).add(new xsltablewrapper(tablename, table));
        }
      }
    } catch (exception e) {
      throw new unitilsexception("unable to create dbunit dataset for data set files: " + arrays.tostring(datasetfiles), e);
    }
    return tablemap;
  }
}

到此,unitils重写及配置完毕,下面进行测试。

测试示例

被测试dao层代码:

public interface membersmapper {
  int deletebyprimarykey(integer id);

  int insert(members record);

  members selectbyprimarykey(integer id);

  int updatebyprimarykey(members record);
}

测试类文件:

import com.agoura.entity.members;
import org.junit.before;
import org.junit.beforeclass;
import org.junit.test;
import org.junit.runner.runwith;
import org.springframework.context.applicationcontext;
import org.springframework.context.support.classpathxmlapplicationcontext;
import org.springframework.test.context.contextconfiguration;
import org.unitils.unitilsjunit4;
import org.unitils.unitilsjunit4testclassrunner;
import org.unitils.dbunit.annotation.dataset;

import static org.junit.assert.assertnotnull;

@runwith(unitilsjunit4testclassrunner.class)
@contextconfiguration(locations = {"classpath*:spring-*.xml"})
public class membersmappertest extends unitilsjunit4 {

  private membersmapper membersmapper;
  private static applicationcontext ctx;

  @beforeclass
  public static void setupbeforeclass() {
    ctx = new classpathxmlapplicationcontext("classpath*:spring-mybatis-unitils.xml");
  }

  @before
  public void setup() {
    membersmapper = (membersmapper) ctx.getbean("membersmapper");
  }

  @test
  @dataset(value = {"test.xls"})   //test.xlsx
  public void testselectbyprimarykey() throws exception {
    members member = membersmapper.selectbyprimarykey(3);
    system.out.println(member);
    assertequals("王五", member.getname());
  }
}

@dataset加载excel文件,既可以加载 .xls文件,也可以加载 .xlsx文件。

.xls示例如下:

应数据库表名,字段必须和数据库表字段一一对应。

测试结果

以上这篇对dbunit进行mybatis dao层excel单元测试(必看篇)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网