对dbunit进行mybatis DAO层Excel单元测试(必看篇)
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单元测试(必看篇)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。