您好,登錄后才能下訂單哦!
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單元測試(必看篇)就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。