首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 数据库 > 其他数据库 >

Unitils 使用指南[通译]-数据库测试

2012-07-19 
Unitils 使用指南[翻译]-数据库测试?1.1.1?????? 数据库测试数据库层的单元测试对构建企业应用来说是比较

Unitils 使用指南[翻译]-数据库测试

?

1.1.1?????? 数据库测试

数据库层的单元测试对构建企业应用来说是比较有价值的,但是由于过于复杂我们不得不放弃他。Unitils降低了数据库测试的复杂度,让数据库测试简单而又容易维护,下面的章节描述DatabaseModule?andDbUnitModule?怎么对你的数据库测试提供支持。

1.1.1.1????使用Dbunit维护测试数据

数据库测试应该使用单元测试数据库,这样你可以完全的精细的控制你使用到的测试数据。DbUnitModule?是基于DBunit构建的,可以提供对测试数据集的支持。

1.1.1.1.1??? 装载测试数据集

让我们看一个例子,UserDao有一个简单的方法findByName,用来通过用户的first 和lastname来取回用户,常用的单元测试如下:

@DataSetpublic class UserDAOTest extends UnitilsJUnit4 {     @Test    public void testFindByName() {        User result = userDao.findByName("doe", "john");        assertPropertyLenientEquals("userName", "jdoe", result);    }     @Test    public void testFindByMinimalAge() {        List<User> result = userDao.findByMinimalAge(18);                assertPropertyLenientEquals("firstName", Arrays.asList("jack"), result);    }}
?

@DataSet注解是通知Unitils查找测试需要加载的Dbunit数据文件。如果没有指定文件名,Unitils会在当前文件夹自动查找和测试类文件名相同的数据集文件如:className.xml.

数据集文件应该使用Dbunit的?FlatXMLDataSet?格式,并且应该包含所有测测试数据。所有表的内容首先被清空,然后所有的测试数据被插入。不在数据文件中的表,是不会被清空内容的。你如果需要清空特定的表你可以在文件中加入一个表的空标签,如:<MY_TABLE />,对于插入null值,你也可以使用类似的方法。

对UserDaoTest你需要建立一个数据集文件名称为:UserDaoTest.xml 并且把它放到UserDaoTest 类文件所在的目录。

<?xml version='1.0' encoding='UTF-8'?><dataset>     <usergroup name="admin" />      <user userName="jdoe"  name="doe"   firstname="john"   userGroup="admin" />        <usergroup name="sales" />        <user userName="smith" name="smith" userGroup="sales" />    </dataset>
?

这会清空user表和usergroup表,并且插入新的记录,用户名为smith的用户的first name会被设置null值。

支持testFindByMinimalAge()方法需要特殊的数据集而不是类级别的数据集。那么你需要建立一个文件:UserDAOTest.testFindByMinimalAge.xml? 并且放在和测试类相同的文件夹即可

<?xml version='1.0' encoding='UTF-8'?><dataset>    <user userName="jack" age="18" />    <user userName="jim"  age="17" /></dataset>
?

你可以用这个数据集文件通过给这个方法上标注@DataSet注解,来覆盖类的数据集文件。

public class UserDAOTest extends UnitilsJUnit4 {     @Test    @DataSet("UserDAOTest.testFindByMinimalAge.xml")    public void testFindByMinimalAge() {        List<User> result = userDao.findByMinimalAge(18);                assertPropertyLenientEquals("firstName", Arrays.asList("jack"), result);    }}
?

方法级别的数据集文件不应该被过度使用,因为过多的数据文件意味这更多的维护工作,你应该尽量减少数据在类级别的数据集,多数情况下比较小的数据集就可以给多个单元测试公用。但是如果公用数据导致了数据量的增大和杂乱,那么用方法级别的数据集,或者细分单元测试类,每个类用自己的数据集。

给一个类或者其父类通过@DataSet设置的数据集对类里的每个测试方法都有效。如果一个数据集只被几个测试方法使用,那么你最好不要把他们放在类级别的注解里,而应该在相应的测试方法上加上注解。如果你的数据集文件没有像我们前面描述的那样命名,你也可以自己命名,当然只需要在@dataset注解中标明即可,你也可以使用多个数据集文件,如下示例:

?

@DataSet({"UserDAOTest_general.xml", "ConfigSettings.xml"})public class UserDAOTest extends UnitilsJUnit4 {     @Test    public void testFindByName() {        User result = userDao.findByName("doe", "john");        assertPropertyLenientEquals("userName", "jdoe", result);    }     @Test     @DataSet("UserDAOTest_ages.xml")    public void testFindByMinimalAge() {        List<User> result = userDao.findByMinimalAge(18);        assertPropertyLenientEquals("firstName", Arrays.asList("jack"), result);    }}
?
1.1.1.1.2 数据集加载策略设置

默认情况下,数据集加载是,采用先清除后插入的策略。那就意味着所有涉及的表中数据会被删除,然后再插入测试数据。这个动作时可以被设置的,你可以通过如下配置来编辑DbUnitModule.DataSet.loadStrategy.default
如果我们在Unitils.properties文件中这么修改:

DbUnitModule.DataSet.loadStrategy.default=org.unitils.dbunit.datasetloadstrategy.InsertLoadStrategy
?

这样设置的话,就不会先删除现有数据了,而只是插入数据。

加载策略也可以对特定的测试类来设置,需要在@DataSet注解中这么标注:

@DataSet(loadStrategy = InsertLoadStrategy.class)
?

CleanInsertLoadStrategy: 先清除后插入策略

  • InsertLoadStrategy: 仅仅插入数据
  • RefreshLoadStrategy: 刷新数据库内容,如果数据库已经有了会使用数据集的数据更新它,如果数据库没有,就会把数据集的输入插入。数据库里有但是数据集中没有的数据不会受到影响。
  • UpdateLoadStrategy: 数据库存在的数据会被更新,但是如果数据集中有但是数据库中没有的话不会有数据变动。
    1.1.1.1.3 配置数据集工厂

    Unitils的数据集文件使用multischema xml? 格式,这是DbUnits?FlatXmlDataSet?格式的一个扩展版本。数据集工厂来管理文件格式的配置和文件扩展。

    尽管Unitils目前只支持一种数据集格式,但是通过自定义的数据集工厂的实现是可以支持不同的文件格式的。
    你可以通过Unitils.propertis文件的DbUnitModule.DataSet.factory.default?? 属性配置或者在@DataSet注解中标明。例如你可以建立一个DbUnit的XlsDataSet通过实现DataSetFactory来使用Excel文件作为数据集文件。

    1.1.1.1.4 验证测试结果

    在测试运行后,有时候使用数据集的数据来对比数据库的内容是比较有用的。例如当你想检查大量数据更新或者存储过程执行的结果。

    下面的例子测试一个方法,这个方法禁用所有一整年没有活动的用户的帐号:

    public class UserDAOTest extends UnitilsJUnit4 {     @Test @ExpectedDataSet    public void testInactivateOldAccounts() {        userDao.inactivateOldAccounts();    }}
    ?

    注意我们在这个方法上增加了@ExpectedDataSet?注解。这会让Unitils寻找数据集文件UserDAOTest.testInactivateOldAccounts-result.xml并且比较数据库内容和数据集的数据。

    <?xml version='1.0' encoding='UTF-8'?><dataset>    <user userName="jack" active="true" />    <user userName="jim"  active="false" /></dataset>
    ?

    对这个数据集,Unitils会检查是否有两个不同的用户记录在用户表。其他的记录或其他表不会涉及。
    使用@DataSet注解,文件名是可以被指定的,如果名字没有指定,那么会查找类似格式文件名的文件:?className.methodName-result.xml
    结果数据集应该尽量小。数据多意味着维护量变大。还有,最好尽量执行相同的检查在测试代码里。

    ?

    1.1.1.1.5 多数据库测试

    一些应用使用了多个数据库。为了实现这个功能,Unitils的数据集XML文件中可以对多数据库进行支持。下面的例子展现了怎么为两个数据库的数据表装载数据。

    <?xml version='1.0' encoding='UTF-8'?><dataset xmlns="SCHEMA_A" xmlns:b="SCHEMA_B">    <user id="1" userName="jack" />        <b:role id="1" roleName="admin" /></dataset>
    ?

    这个例子我们定义了两个方案,A和B,schema-A和默认的XML命名空间关联,schema B关联命名空间b.如果一个表的描述中有前缀指明哪个命名空间,那么就会用指定的,否则默认schema A。

    如果没有默认的命名空间,那么系统会默认在database.schemaNames属性中第一个schema。所以系统是支持你如下定义的。

    database.schemaNames=SCHEMA_A, SCHEMA_B

    这样系统默认schema-A 为默认的schema,那么你就可以不用再声明默认的schema了。

    <?xml version='1.0' encoding='UTF-8'?><dataset xmlns:b="SCHEMA_B">    <user id="1" userName="jack" />        <b:role id="1" roleName="admin" /></dataset>
    ?
    1.1.1.1.6 连接测试数据库

    database.driverClassName=oracle.jdbc.driver.OracleDriverdatabase.url=jdbc:oracle:thin:@yourmachine:1521:YOUR_DBdatabase.userName=johndatabase.password=secretdatabase.schemaNames=test_john?

    通常驱动和url配置放在Unitils.properties文件里作为项目共用,用户密码可以放在Unitils-local.properties作为每个开发者自己的设置。这样可以让每个开发者使用自己的测试数据,防止互相干扰。

    当一个测试开始执行的时候,数据源示例会被注入到测试实例中,如果一个属性或者setter方法用注解@TestDataSource指定了,那么系统会使用你指定的数据源。你仍需要提供一些项目特定的代码来让你的代码使用这个数据源。通常这些配置在项目父类中被执行一次,简单的示例如下:

    public abstract class BaseDAOTest extends UnitilsJUnit4 {     @TestDataSource    private DataSource dataSource;        @Before        public void initializeDao() {        BaseDAO dao = getDaoUnderTest();        dao.setDataSource(dataSource);    }        protected abstract BaseDAO getDaoUnderTest();}
    ?

    The above example usesannotations to get a reference to the datasource. Another way of making yourcode use the Unitils?DataSource?is by callingDatabaseUnitils.getDataSource().

    上面的例子使用注解获取一个指定的数据源。另一个使用UnitilsDataSource的方法是使用DatabaseUnitils.getDataSource().

    1.1.1.1.7 事务处理

    很多情况下我们我们要以事务的方式存取数据,例如:

  • 数据库动作仅在事务执行的时候存在,如使用?SELECT FOR UPDATE 或者提交的时候有触发器行为
  • 许多项目运行测试前,需要先准备一些general-purpose数据,每个测试前数据会有插入或修改。为了确保数据库在每个测试前都有一个已知的明确的状态,那么在每个测试前事务要开始,执行完以后事务需要回滚。
  • 当我们使用hibernate或者JPA,他可能要求你使用事务为每个测试,来保证系统的正常运行。

    By default every test isexecuted in a transaction, which is committed at the end of the test.

    默认状况下每个以事务方式执行的测试,完成后数据会被提交。

    默认的动作可以通过配置来修改,如:

    DatabaseModule.Transactional.value.default=disabled
    ?

    可选的值还有:?commit,?rollback?and?disabled.

    事务行为可以在测试类中被修改,会用到?@Transactional.注解如:

    @Transactional(TransactionMode.ROLLBACK)public class UserDaoTest extends UnitilsJUnit4 {
    ?

    这样这个测试类的每个测试执行后数据将回滚。@Transactional?注解是可以被继承的。如果有必要你可以在你测试类的父类中使用。

    其实,Unitils依赖Spring的事务管理,但这不意味着你必须使用Spring在你的应用代码里。实际上对用户来说那是透明的。

    如果你继承了Unitils和Spring,并且你已经配置了bean的类型PlatformTransactionManager?在Spring配置里。Unitils会使用这个事务管理器。

    ?

  • 热点排行