美文网首页
DBunit + H2实战

DBunit + H2实战

作者: MonkeySolider | 来源:发表于2020-05-23 16:43 被阅读0次

    0. 概述

    DBUnit是一种基于JUnit的数据库驱动测试框架。DBUnit 的设计理念就是在测试之前,给对象数据库植入我们需要的准备数据,最后,在测试完毕后,回溯到测试前的状态。它使数据库在测试过程之间处于一种已知状态,如果一个测试用例对数据库造成了破坏性影响,它可以帮助避免造成后面的测试失败或者给出错误结果。spring-test-dbunit提供了Spring Test Framework与DBUnit之间的集成。使用Spring Test DbUnit提供了注解驱动的数据库集成测试方式。
    DBUnit的实施步骤:

    1. 根据业务,做好测试用的准备数据和预想结果数据,通常准备成xml 格式文件。
    2. 在setUp() 方法里边备份数据库中的关联表。
    3. 在setUp() 方法里边读入准备数据。
    4. 对测试类的对应测试方法进行实装: 执行对象方法,把数据库的实际执行结果和预想结果进行比较。
    5. 在tearDown() 方法里边, 把数据库还原到测试前状态。
    

    但是仅使用DBUnit会操作真实的Db,测试之前会清空数据库,会影响其他同学的自测、QA用例的跑通等等。本文介绍将内存数据库用于DbUnit,保证数据库的隔离性与可重复执行的同时,也不会影响到其他同事。

    1. 依赖引入

    <dependency>
        <groupId>org.dbunit</groupId>
        <artifactId>dbunit</artifactId>
        <version>2.5.0</version>
        <type>jar</type>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.github.springtestdbunit</groupId>
        <artifactId>spring-test-dbunit</artifactId>
        <version>1.2.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.3.157</version>
    </dependency>
    

    2. 定义基类

    定义一个基类,在基类上面使用@TestExecutionListeners注解注册Spring Test Dbunit监听器,用于指定在测试类执行之前,可以做的一些动作,这里处理两个listener,监视器中配置2个listener,其他所有测试类继承该基类

    关于TestExecutionListener用法 其他Listener配置类

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = ApplicationLoader.class)
    @TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
            DbUnitTestExecutionListener.class})
    public abstract class BaseTest {
      
    }
    

    2.1 DependencyInjectionTestExecutionListener

    对测试中的类进行依赖注入
    下面是spring-test 5.0.11.RELEASE默认使用的listener,所以不使用@TestExecutionListeners注解时,spring支持自动处理依赖注入(类TestContextManager中可以查看到容器启动时加载的Listener),当使用了该注解时,需要手动添加依赖注册listener

    # Default TestExecutionListeners for the Spring TestContext Framework
    #
    org.springframework.test.context.TestExecutionListener = \
        org.springframework.test.context.web.ServletTestExecutionListener,\
        org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener,\
        org.springframework.test.context.support.DependencyInjectionTestExecutionListener,\
        org.springframework.test.context.support.DirtiesContextTestExecutionListener,\
        org.springframework.test.context.transaction.TransactionalTestExecutionListener,\
        org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener
    

    2.2 DbUnitTestExecutionListener

    处理带有@DbUnitConfiguration注解的类,用于配置DataSource

    3. 编写测试类

    编写测试类,该类继承基类。并在改测试类加上@DbUnitConfiguration注解和@DatabaseSetup注解

    @DbUnitConfiguration(databaseConnection = "h2UnifiedCouponGroupDataSource")
    @DatabaseSetup(value = {"classpath:config/spring/database/CouponGroup.xml"}, connection = "h2UnifiedCouponGroupDataSource", type = DatabaseOperation.INSERT)
    public class UnifiedCouponGroupDAOTest extends BaseTest {
        
        @Resource
        UnifiedCouponGroupDAO unifiedCouponGroupDAO;
        
        @Test
        public void getCoupon() throws Exception {
            CouponGroup coupon = unifiedCouponGroupDAO.getCoupon(328627430);
            Assert.assertEquals(328627430, (int) coupon.getCoupongroupid());
        }
    }
    

    3.1 DbUnitConfiguration用于配置数据源

    如果不配置spring-test-dbunit数据源,则会在容器中查找id="dbUnitDatabaseConnection"或是"dataSource"的数据源。这里不使用zebra(美团开源)数据源,使用h2数据源,配置文件如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"
           default-autowire="byName">
    
        <!--h2 DataSource-->
        <bean id="h2UnifiedCouponGroupDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="org.h2.Driver" />
            <property name="url" value="jdbc:h2:mem:PCTDiscount;MODE=MYSQL;DB_CLOSE_DELAY=-1" />
        </bean>
    
        <bean id="unifiedCouponGroupSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="h2UnifiedCouponGroupDataSource"/>
            <property name="mapperLocations">
                <list>
                    <value>classpath:config/mapper/discount/auto/*.xml</value>
                </list>
            </property>
            <!-- 配置mybatis配置文件的位置 -->
            <property name="configLocation" value="classpath:config/mybatis/mybatis-configuration.xml"/>
        </bean>
    
        <!-- 配置扫描Mapper接口的包路径 -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="unifiedCouponGroupSessionFactory" />
            <property name="basePackage" value="com.dianping.unified.coupon.dal.dao.mapper.discount"/>
        </bean>
    </beans>
    

    如果需要配置多数据源(spring-test-dbunit 1.2.0之后的版本才可以配置多数据源),则配置如下
    @DbUnitConfiguration(databaseConnection={"h2DataSource","h2DataSource_2"})

    3.2 DatabaseSetup

    将特定xml文件中的数据同步到数据库,这个注解可以放在整个测试类上或者单个测试方法上,如果放在类上,则对所有方法都有效。如果不需要准备初始数据,可以不用此注解。

    3.2.1 value:数据集文件

    测试执行之前设置数据库初始状态的数据集(DataSet)文件,是标准的DbUnit XML文件(可以利用sequel pro的bundles实现生成xml文件https://github.com/alsbury/SequelProCopyPHPUnitDataset

    <?xml version="1.0" encoding="UTF-8"?>
    <dataset>
        <My_Table
                ID="30"
                Name="xxx"
        />
    </dataset>
    

    3.2.2 type:对数据库的操作类型

    如果不设置默认是DatabaseOperation.CLEAN_INSERT

    public enum DatabaseOperation {
      
      //将数据集中的内容更新到数据库中。它假设数据库中已经有对应的记录,否则将失败。
      UPDATE,
      
      //将数据集中的内容插入到数据库中。它假设数据库中没有对应的记录,否则将失败。
      INSERT,
      
      //将数据集中的内容刷新到数据库中。如果数据库有对应的记录,则更新,没有则插入。
      REFRESH,
      
      //删除数据库中与数据集对应的记录。
      DELETE,
      
      //删除表中所有的记录,如果没有对应的表,则不受影响。
      DELETE_ALL,
      
      //与DELETE_ALL类似,更轻量级,不能rollback。
      TRUNCATE_TABLE,
      
      //是一个组合操作,是DELETE_ALL和INSERT的组合
      CLEAN_INSERT;
    }
    

    3.2.3 connection:连接数据源

    必须是@DbUnitConfiguration中配置的数据源,如果不指定,默认是@DbUnitConfiguration配置的第一个数据源。Spring-test-dbunit 1.2.0之后才支持。

    3.2.4 其他注解

    与@DatabaseSetup相对应的还有下面两个注解

    • @DatabaseTearDown:清理数据
      测试完成之后,用指定数据库去重置数据库

    • @ExpectedDatabase :数据验证
      通常的验证方式是更新后再去查询数据库再做比对。而DBUnit支持你将一个预期结果写到xml文件中,当测试用例更新完后会和数据库中的数据自动做比较,看看是否符合预期
      默认比对模式为DatabaseAssertionMode.DEFAULT

    public enum DatabaseAssertionMode {
    
        //要验证所有的字段
        DEFAULT(new DefaultDatabaseAssertion()),
    
      //支持只验证部分字段,将忽略没有在期望数据集中出现,但是在实际数据集中出现的表和列名
        NON_STRICT(new NonStrictDatabaseAssertion()),
    
        //支持只验证部分字段,将忽略没有在期望数据集中出现,但是在实际数据集中出现的表和列名;且支持数据的行排序不同
        NON_STRICT_UNORDERED(new NonStrictUnorderedDatabaseAssertion());
    }
    

    4. H2数据库

    使用spring schema jdbc:initialize-database 初始化数据库。

    <!-- 初始化数据库 -->
    <jdbc:initialize-database data-source="h2UnifiedCouponGroupDataSource" ignore-failures="DROPS">
        <jdbc:script location="classpath:config/spring/sql/mytable-ddl.sql" />
        <jdbc:script location="classpath:config/spring/sql/mytable*-dml.sql" /> 
    </jdbc:initialize-database>
    

    4.1 DDL

    h2数据库对mysql的ddl语句的语法不是完全支持,目前已发现的一些不支持的情况:

    • 不支持表级别comment
    • 字段不支持 COLLATE utf8_bin
    • 字段不支持CHARACTER SET utf8mb4
    • 字段不支持CHARACTER SET utf8
    • 字段不支持ON UPDATE CURRENT_TIMESTAMP

    4.2 DML

    如果使用@DatabaseSetup,指定xml的数据源,则可以不执行dml.sql。(dml.sql也可以使用sequel pro的Copy as SQL INSERT生成)
    如果同时使用xml和dml,注意数据的主键、唯一键冲突

    5. 终极偷懒指南

    骚操作:使用spring-boot,省略上面所有注解配置,直接配置h2数据源(所有db的mapper均在该xml配置)、使用sql初始化数据。
    FAQ:

    6. 总结

    使用DBUnit+H2数据库,可以满足数据隔离特性,保证数据可重复执行,同时不会操作真实DB

    7. 参考

    DbUnit学习笔记
    DbUnit实践:Spring Test Dbunit,H2数据库

    相关文章

      网友评论

          本文标题:DBunit + H2实战

          本文链接:https://www.haomeiwen.com/subject/rdklahtx.html