关于单元测试之王

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于单元测试之王相关的知识,希望对你有一定的参考价值。

第一步:pom依赖

技术分享图片
<!-- 单元测试 -->
            <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>

            <dependency>
                <groupId>org.easymock</groupId>
                <artifactId>easymock</artifactId>
                <version>3.5.1</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.easymock</groupId>
                <artifactId>easymock</artifactId>
                <version>RELEASE</version>
            </dependency>

            <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>
pom

第二步:编写关于unitils的4个工具类,我现在放在unitils包下

技术分享图片
package com.odianyun.back.order.web.unitils;

import java.io.File;
import java.util.Arrays;
import java.util.Properties;

import org.unitils.core.UnitilsException;
import org.unitils.dbunit.datasetfactory.DataSetFactory;
import org.unitils.dbunit.util.MultiSchemaDataSet;

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";
    }
}
MultiSchemaXlsDataSetFactory
技术分享图片
package com.odianyun.unitils;

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;
    }
}
MultiSchemaXlsDataSetReader
技术分享图片
package com.odianyun.unitils;

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;
    }
}
MyDbUnitModule
技术分享图片
package com.odianyun.unitils;

import org.apache.commons.lang.StringUtils;
import org.dbunit.dataset.*;
import org.unitils.core.UnitilsException;

class XslTableWrapper extends AbstractTable {
    private ITable delegate;
    private String tableName;

    public XslTableWrapper(String tableName, ITable table) {
        this.delegate = table;
        this.tableName = tableName;
    }
    public int getRowCount() {
        return delegate.getRowCount();
    }

    public ITableMetaData getTableMetaData() {
        ITableMetaData meta = delegate.getTableMetaData();
        try {
            return new DefaultTableMetaData(tableName, meta.getColumns(), meta.getPrimaryKeys());
        } catch (DataSetException e) {
            throw new UnitilsException("Don‘t get the meta info from  " + meta, e);
        }
    }

    public Object getValue(int row, String column) throws DataSetException {
        Object delta = delegate.getValue(row, column);
        if (delta instanceof String) {
            if (StringUtils.isEmpty((String) delta)) {
                return null;
            }
        }
        return delta;
    }

}
XslTableWrapper

第三步:在test下创建一个dbscripts的sql的ddl文件

我就随便到处了数据结构的

技术分享图片
SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) COLLATE utf8_bin DEFAULT NULL,
  `status` int(2) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
init.sql

第四步:配置

技术分享图片
unitils.modules=database,dbunit,spring

#自定义扩展模块,加载Excel文件,默认拓展模块org.unitils.dbunit.DbUnitModule支持xml
#unitils.module.dbunit.className=org.unitils.dbunit.DbUnitModule
unitils.module.dbunit.className=com.odianyun.unitils.MyDbUnitModule

#配置数据库连接
database.driverClassName=com.mysql.jdbc.Driver
database.url=jdbc:mysql://127.0.0.1:3306/dbtest?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=utf-8
database.userName=root
database.password=root
#配置为数据库名称
database.schemaNames=dbtest
#配置数据库方言
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/test/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=com.odianyun.unitils.MultiSchemaXlsDataSetFactory
DbUnitModule.ExpectedDataSet.factory.default=com.odianyun.unitils.MultiSchemaXlsDataSetFactory

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

#配置数据集结构模式XSD生成路径,可以自定义目录,但不能为空
dataSetStructureGenerator.xsd.dirName=src/test/resources/xsd
dbMaintainer.generateDataSetStructure.enabled=true

#文件相对路径是否是测试类文件路径,false表示resource根目录
dbUnit.datasetresolver.prefixWithPackageName=false
unitils.properties

第五步:创建BaseUnitsTest类

技术分享图片
package com.test;

import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.unitils.UnitilsJUnit4TestClassRunner;

/**
 * @author 危常焕
 * @time 2017/11/28
 * @description https://github.com/newsoulbr/mokitopoc/blob/master/src/test/java/com/github/newsoulbr/mokitopoc/service/PersonServiceTest.java
 *              http://www.unitils.org/tutorial-reflectionassert.html
 *              http://blog.csdn.net/achuo/article/details/47726241
 *
 */
@RunWith(UnitilsJUnit4TestClassRunner.class)
public class BaseUnitilsTest {

    protected static ApplicationContext ctx;
    protected static ConfigurableApplicationContext configurableContext;
    protected static BeanDefinitionRegistry beanDefinitionRegistry;

    @BeforeClass
    public static void setUpBeforeClass() {

        ctx = new ClassPathXmlApplicationContext("classpath*:applicationContext.xml");
        configurableContext = (ConfigurableApplicationContext) ctx;
        beanDefinitionRegistry = (DefaultListableBeanFactory) configurableContext.getBeanFactory();
    }

    public static void registerBean(String beanId, String className) {
        // get the BeanDefinitionBuilder
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(className);
        // get the BeanDefinition
        BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        // register the bean
        beanDefinitionRegistry.registerBeanDefinition(beanId, beanDefinition);
    }

    /**
     * 移除bean
     * 
     * @param beanId
     *            bean的id
     */
    public static void unregisterBean(String beanId) {
        beanDefinitionRegistry.removeBeanDefinition(beanId);
    }
}
BaseUnitilsTest

 

第六步:准备excel:sheet是代表表名:第一列是列名:一行是一个对象

第七步:测试

技术分享图片
package com.test;

import com.po.SysRoleResource;
import com.service.SysRoleResourceService;
import com.service.impl.SysRoleResourceServiceImpl;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.unitils.dbunit.annotation.DataSet;

public class TestDemo2 extends BaseUnitilsTest {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private SysRoleResourceService sysRoleResourceService;

    @Before
    public void setUp() {
        sysRoleResourceService = (SysRoleResourceService) ctx.getBean(SysRoleResourceServiceImpl.class);
    }

    @Test
    @DataSet(value = { "excel\\demo.xls" })
    // 通过Unitils提供的@DataSet注解从当前测试类this.class所在的目录寻找支持DbUnit的数据集文件并进行加载。
    // 执行测试逻辑之前,会把加载的数据集先持久化到测试数据库中,
    // @ExpectedDataSet(value = {
    // "com\\odianyun\\demo\\dao\\WarehouseDAOTestdemo.xls" })
    // 是在单元测试之后进行数据比对
    // 从当前测试类this.class所在的目录寻找支持DbUnit的验证数据集文件并进行加载,
    // 之后验证数据集里的数据和数据库中的数据是否一致
    public void selectByPrimaryKey() {
        SysRoleResource selectByPrimaryKey = sysRoleResourceService.selectByPrimaryKey(1);
        System.err.println(selectByPrimaryKey.getRoleId());
        logger.debug(selectByPrimaryKey.getRoleId() + "");

    };

}
test类

 

以上是关于关于单元测试之王的主要内容,如果未能解决你的问题,请参考以下文章

关于单元测试,如何写出可测试的代码?

原!关于java 单元测试的一些总结

单元测试不了解 XCTest 期望的异步 UI 代码?

四则运算单元测试

单元测试很棒,但是

词频统计单元测试