1、unitils測試框架優缺點介紹java
在unitils的支持下,xml配置的spring項目在測試時,有以下好處:mysql
一、利用註解@DataSet、@ExpectedDataSet來準備數據和校驗結果數據,每次運行測試用例不用從新準備數據。git
二、利用@Transactional來配置測試時的事務模式:COMMIT(測試後提交數據)、ROLLBACK(測試後回滾數據,不會污染測試數據庫)github
三、能夠基於單獨的單元測試數據庫進行單元測試spring
四、等等sql
利用unitils來進行單元測試,好處多多,單元測試對於開發人員來講,相當重要,首先能夠檢查程序是否按照預期運行,其次,過段時間後,只須要運行一下單元測試,就能夠檢車程序是否被修改出錯,及時檢查出問題。數據庫
可是,上述基於unitils的spring項目的單元測試都是在xml配置文件的基礎之上,目前不少項目都是spring-boot項目,最新的unitils-3.4.6也不支持spring-boot的項目,所以,這裏在查看unitils的源碼的基礎上,作了一個小的開源項目:spring-boot-unitils-starter來給spring-boot項目提供基於unitils支持。mybatis
2、spring-boot-unitils-starter項目介紹oracle
一、項目結構以下app
二、問題分析
在一中介紹到了unitils只支持xml形式的spring 項目,spring-boot項目是去配置化了,並且查看unitils的源碼得知,測試中用到的bean都是從一個applicationContext中獲取以後再設置到對應的屬性(經過註解@SpringBeanByType、@SpringBeanByName來實現)中,所以spring-boot-unitils-starter項目的重點就是將spring-boot應用的applicationContext獲取到而後設置到unitils中的applicationContext。
三、主要功能點
3.一、spring-boot應用applicationContext的獲取與設置
@Configuration @ConditionalOnClass(UnitilsBootBlockJUnit4ClassRunner.class) public class ConfigurableApplicationContextAware implements InitializingBean { @Autowired private ConfigurableApplicationContext configurableApplicationContext; @Override public void afterPropertiesSet() throws Exception { SpringBootModule.setApplicationContext(configurableApplicationContext); } }
這裏的@ConditionalOnClass就是爲了在測試的時候才聲明這個bean(ConfigurableApplicationContextAware),UnitilsBootBlockJUnit4ClassRunner是單元測試的ClassRunner。
3.二、spring項目運行unitils的核心類SpringBootModule
原始的unitils提供的是SpringModule,applicationContext就是該類的一個屬性,而且只能get,不能set,爲了和原始的SpringModule區分開,而且考慮到咱們測試的應用是spring-boot,所以這裏就新建了SpringBootModule,修改applicationContext爲static,而且給applicationContext設置了靜態set方法。
private static ApplicationContext applicationContext; public static void setApplicationContext(ApplicationContext applicationContext) { SpringBootModule.applicationContext = applicationContext; }
3.三、SpringBootApplicationContextFactory的做用就是去掉xml的配置,避免啓動出錯
public class SpringBootApplicationContextFactory implements ApplicationContextFactory { private static ConfigurableApplicationContext configurableApplicationContext ; public ConfigurableApplicationContext createApplicationContext(List<String> locations) { return configurableApplicationContext; } public static void setConfigurableApplicationContext(ConfigurableApplicationContext configurableApplicationContext) { SpringBootApplicationContextFactory.configurableApplicationContext = configurableApplicationContext; } }
這裏的setConfigurableApplicationContext方法也能夠設置applicationContext。
3.四、結合spring和unitils的classRunner:UnitilsBootBlockJUnit4ClassRunner
這個類是結合了原始unitils中的UnitilsBlockJUnit4ClassRunner和spring-test中的SpringJUnit4ClassRunner,使其在初始化spring容器後,還能夠unitils相關的初始化。
3.五、添加xls文件支持:MultiSchemaXlsDataSetReader和MultiSchemaXlsDataSetFactory
MultiSchemaXlsDataSetReader和MultiSchemaXlsDataSetFactory添加後,就能夠支持@DataSet中的xls文件。
3.六、替換datasource:DataSourcePostProcessor
測試時,替換容器中的datasource,使用unitils來進行事務管理
@Component public class DataSourcePostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("dataSource")) { try { return new UnitilsDataSourceFactoryBean().getObject(); } catch (Exception exp) { throw new RuntimeException("replace database throw exception ,can not continue to process", exp); } } return bean; } }
3、測試樣例
一、unitils.properties配置文件
unitils.modules=database,dbunit,springBoot unitils.module.springBoot.className=com.unitils.boot.SpringBootModule unitils.module.springBoot.runAfter=database unitils.module.springBoot.enabled=true #自擴展模塊 unitils.module.dbunit.className=org.unitils.dbunit.DbUnitModule ############################################################################ ### Database模塊相應配置 ### ############################################################################ ## Full qualified class name of an implementation of org.unitils.database.datasource.DataSourceFactory. This class is used # to provide a DataSource for all database unit tests and for the DBMaintainer. org.unitils.database.datasource.DataSourceFactory.implClassName=org.unitils.database.datasource.impl.DefaultDataSourceFactory #數據庫事務類型 #可選:commit/rollback/disanled database.default.transaction.mode=commit ## 測試數據庫 # 此數據庫驅動類型 database.driverClassName=com.mysql.jdbc.Driver # 此數據庫鏈接信息 database.url=jdbc:mysql://127.0.0.1/test # 此數據庫鏈接用戶名 database.userName=root # 此數據庫鏈接用戶密碼 database.password=12345678 # 此數據庫鏈接的schema database.schemaNames=test # 此數據庫數據庫類型:oracle/mysql/postgres等 database.dialect=mysql # 不一樣數據庫對應的實現 # Fully qualified classnames of the different, dbms specific implementations of org.dbmaintain.database.Database.implClassName org.dbmaintain.database.Database.implClassName.oracle=org.dbmaintain.database.impl.OracleDatabase org.dbmaintain.database.Database.implClassName.mysql=org.dbmaintain.database.impl.MySqlDatabase # 是否支持初數據庫始化腳本,默認關閉(能夠經過腳本每次重建數據庫等) # The database maintainer is disabled by default. updateDataBaseSchema.enabled=true #This table is by default not created automatically dbMaintainer.autoCreateExecutedScriptsTable=true # Indicates whether a from scratch update should be performed when the previous update failed, but # none of the scripts were modified since that last update. If false a new update will be tried only when # changes were made to the script files. dbMaintainer.keepRetryingAfterError.enabled=true dbMaintainer.script.locations= ############################################################################ ### Database模塊相應配置 ### ############################################################################ # Dbunit中DataSet和ExpectedDataSet的數據準備實現類,(也能夠用Excel準備數據,須要替換實現類) DbUnitModule.DataSet.factory.default=com.unitils.boot.xls.MultiSchemaXlsDataSetFactory DbUnitModule.ExpectedDataSet.factory.default=com.unitils.boot.xls.MultiSchemaXlsDataSetFactory org.dbunit.database.IMetadataHandler.implClassName=org.dbunit.ext.mysql.MySqlMetadataHandler ## Dbunit中測試數據處理策略 # CleanInsertLoadStrategy:先刪除dateSet中有關表的數據,而後再插入數據。 # InsertLoadStrategy:只插入數據。 # RefreshLoadStrategy:有一樣key的數據更新,沒有的插入。 # UpdateLoadStrategy: 有一樣key的數據更新,沒有的不作任何操做。 DbUnitModule.DataSet.loadStrategy.default=org.unitils.dbunit.datasetloadstrategy.impl.CleanInsertLoadStrategy # XSD generator dataSetStructureGenerator.xsd.dirName=/tmp/resources/xsd SpringModule.applicationContextFactory.implClassName=com.unitils.boot.util.SpringBootApplicationContextFactory
module的配置:
unitils.modules=database,dbunit,springBoot unitils.module.springBoot.className=com.unitils.boot.SpringBootModule
第一行指定了module,springBoot是spring-boot-unitils-starter中的,unitils.module.springBoot.className指定了其對應的類。
database.driverClassName=com.mysql.jdbc.Driver # 此數據庫鏈接信息 database.url=jdbc:mysql://127.0.0.1/test # 此數據庫鏈接用戶名 database.userName=root # 此數據庫鏈接用戶密碼 database.password=12345678 # 此數據庫鏈接的schema database.schemaNames=test # 此數據庫數據庫類型:oracle/mysql/postgres等 database.dialect=mysql
上述指定了單元測試數據庫,通常只有這裏根據本身的狀況修改。
DbUnitModule.DataSet.factory.default=com.unitils.boot.xls.MultiSchemaXlsDataSetFactory DbUnitModule.ExpectedDataSet.factory.default=com.unitils.boot.xls.MultiSchemaXlsDataSetFactory SpringModule.applicationContextFactory.implClassName=com.unitils.boot.util.SpringBootApplicationContextFactory
上述指定了@DataSet、@ExpectedDataSet的文件解析和applicationContextFactory的類位置,這裏若是是xls形式的數據文件,則不用修改,若是是xml形式的數據,只須要修改以下的配置值便可
DbUnitModule.DataSet.factory.default=com.unitils.boot.xls.MultiSchemaXlsDataSetFactory DbUnitModule.ExpectedDataSet.factory.default=com.unitils.boot.xls.MultiSchemaXlsDataSetFactory
二、測試主類
package com.unitils.boot.controller; import com.unitils.boot.SampleTestApplication; import com.unitils.boot.util.UnitilsBootBlockJUnit4ClassRunner; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.unitils.database.annotations.Transactional; import org.unitils.database.util.TransactionMode; import org.unitils.dbunit.annotation.DataSet; import org.unitils.spring.annotation.SpringBeanByType; @RunWith(UnitilsBootBlockJUnit4ClassRunner.class) @SpringBootTest(classes = SampleTestApplication.class) @Transactional(value = TransactionMode.ROLLBACK) public class UserControllerTest { @SpringBeanByType private UserController userController ; @Test @DataSet(value = {"/data/getUserInfo.xls"}) public void test_getUsername(){ String username = userController.getUsername(3); Assert.assertNotNull(username); Assert.assertTrue(username.equals("wangwu")); } }
註解@RunWith指定了咱們定製的ClassRunner:UnitilsBootBlockJUnit4ClassRunner,@SpringBootTest指定了spring-boot的應用啓動類:SampleTestApplication,由於咱們須要讓spring容器識別到ConfigurableApplicationContextAware,所以,須要新建一個類來讓ConfigurableApplicationContextAware(在包com.unitils.boot.autoconfigure下)被掃描到,這裏新建了SampleTestApplication,其代碼以下:
package com.unitils.boot; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @ComponentScan({"com.unitils.boot", "com.unitils.boot.autoconfigure"}) @MapperScan(basePackages = "com.unitils.boot.mapper") public class SampleTestApplication { public static void main(String[] args) { SpringApplication.run(SampleTestApplication.class, args); } }
三、數據準備文件getUserInfo.xls
列名就是表的列名,sheet的名字就是表名。
4、擴展
項目只是在每次跑一個單元測試方法的時候用過,其餘場景還沒用過,不知道會出什麼問題;項目也沒通過完備的測試,可能存在未知BUG,後續後有針對性的完善。
項目已經通過測試,知足單元測試需求,在單個和多個測試同時跑的狀況下,表現良好。
在此基礎之上修改獲得的成果是:spring-boot-unitils-starter能夠在maven中央庫下載了,對應的maven依賴以下:
<dependency> <groupId>com.github.yangjianzhou</groupId> <artifactId>spring-boot-unitils-starter</artifactId> <version>1.1.0.RELEASE</version> </dependency>
附:項目的完整代碼見:https://github.com/yangjianzhou/spring-boot-unitils