給spring-boot測試提供unitils支持的開源項目

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

相關文章
相關標籤/搜索