DBunit入门

06 September 2010

 

使用DBTestCase子类设置数据库

第1步:创建你的数据集文件

你的测试需要一些数据来辅助。也就是说你必须创建一个数据集。在大多数情况下你会使用XML数据集。你可以手动从头创建一个flat XML数据集或从你的数据库中导出一些数据。

第2步:扩展类的DBTestCase

现在您需要创建一个测试类。最简单的使用DBUnit的方法是是你的测试类继承DBTestCase。DBTestCase扩展了JUnit TestCase类。一个getDataSet()模板方法必须被实现,该方法返回步骤1中创建的数据集。DBTestCase依赖IDatabaseTester来完成工作,默认配置使用PropertiesBasedJdbcDatabaseTester,它通过系统属性来定位DriverManager的配置。最简单的配置方法是通过测试类的构造函数来配置。你可以通过使用你自己的或其他3个系统提供的IDatabaseTester实现重载getDatabaseTester()来改变该行为。

你还可以使用如下所述其他的DBTestCase子类:

JdbcBasedDBTestCase 使用一个DriverManager创建连接(在JdbcDatabaseTester的辅助之下)。
DataSourceBasedDBTestCase 使用javax.sql.DataSource创建连接(在DataSourceDatabaseTester的辅助之下)。
JndiBasedDBTestCase 使用javax.sql.DataSource通过JNDI定位(在JndiDatabaseTester的辅助之下)。

 

以下是一个示例实现,返回一个Hypersonic数据库连接和一个XML数据集:

public class SampleTest extends DBTestCase
{
    public SampleTest(String name)
    {
        super( name );
        System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS, "org.hsqldb.jdbcDriver" );
        System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL, "jdbc:hsqldb:sample" );
        System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME, "sa" );
        System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD, "" );
	// System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_SCHEMA, "" );
    }

    protected IDataSet getDataSet() throws Exception
    {
        return new FlatXmlDataSetBuilder().build(new FileInputStream("dataset.xml"));
    }
}

 

第3步:(可选)实现getSetUpOperation()和getTearDownOperation()方法

默认情况下,DBUnit的执行每个测试之前回进行CLEAN_INSERT操作并且在之后没有清除操作。你可以通过重载getSetUpOperation()和getTearDownOperation()来改变这一行为。

下面的示例展现了你可以很简单的重载测试前后的操作

public class SampleTest extends DBTestCase
{
    ...
    protected DatabaseOperation getSetUpOperation() throws Exception
    {
        return DatabaseOperation.REFRESH;
    }

    protected DatabaseOperation getTearDownOperation() throws Exception
    {
        return DatabaseOperation.NONE;
    }
    ...
}

 

第4步:(可选)重载方法setUpDatabaseConfig(DatabaseConfig congfig)

使用如下方法来改变DBUnit的DatabaseConfi中g某些配置设置。

下面的示例演示如何你可以轻松地重写此方法:

public class SampleTest extends DBTestCase
{
    ...
    /**
     * Override method to set custom properties/features
     */
    protected void setUpDatabaseConfig(DatabaseConfig config) {
        config.setProperty(DatabaseConfig.PROPERTY_BATCH_SIZE, new Integer(97));
        config.setFeature(DatabaseConfig.FEATURE_BATCHED_STATEMENTS, true);
    }
    ...
}

 

第5步:实现你的testXXX()方法

像正常使用JUnit一样实现你的测试方法。现在你的数据库将按照你之前几步所做设置那样在每个测试方法之前进行初始化并在之后进行清除操作。

用你自己的TestCase子类来是设置数据库

使用Dbunit并不严格要求继承DBTestCase。你可以重写标准的JUnit的setUp()方法并在数据库上执行所需的操作。如果您需要执行清理还可以做一些类似tearDwon()的操作。

例如:

public class SampleTest extends TestCase
{
    public SampleTest(String name)
    {
        super(name);
    }

    protected void setUp() throws Exception
    {
        super.setUp();

        // initialize your database connection here
        IDatabaseConnection connection = null;
        // ...

        // initialize your dataset here
        IDataSet dataSet = null;
        // ...

        try1
        {
            DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
        }
        finally
        {
            connection.close();
        }
    }
    ...
}

 

自2.2版你可以使用新的IDatabaseTester来完成相同的壮举。如前所述,DBTestCase使用IDatabaseTester在内部进行工作,你的测试类也可以使用此方法来操纵数据集。目前有4个已有的实现:

JdbcDatabaseTester 使用一个DriverManager来创建连接。
PropertiesBasedJdbcDatabaseTester 同样使用DriverManager,但配置是采取从系统性能。
DBTestCase的默认实现。
DataSourceDatabaseTester 使用javax.sql.DataSource创建连接。
JndiDatabaseTester 使用javax.sql.DataSource通过JNDI定位的。

你也可以自己实现IDatabaseTester接口。建议以AbstractDatabaseTester作为出发点。

示例:

public class SampleTest extends TestCase
{
    private IDatabaseTester databaseTester;

    public SampleTest(String name)
    {
        super(name);
    }

    protected void setUp() throws Exception
    {
        databaseTester = new JdbcDatabaseTester("org.hsqldb.jdbcDriver",
            "jdbc:hsqldb:sample", "sa", "");

        // initialize your dataset here
        IDataSet dataSet = null;
        // ...

        databaseTester.setDataSet( dataSet );
	// will call default setUpOperation
        databaseTester.onSetUp();
    }

    protected void tearDown() throws Exception
    {
	// will call default tearDownOperation
        databaseTester.onTearDown();
    }
    ...
}

 

数据库数据检查

DBUnit支持校验两个表或者数据集中包含的数据是否相同。以下两种方法可以用来验证在测试用例执行期间你的数据库是否包含预期的数据。

public class Assertion
{
    public static void assertEquals(ITable expected, ITable actual)
    public static void assertEquals(IDataSet expected, IDataSet actual)
}

 

示例

下面的示例,说明如何对数据库表的快照和XML数据表进行比较

public class SampleTest extends DBTestCase
{
    public SampleTest(String name)
    {
        super(name);
    }

    // Implements required setup methods here
    ...

    public void testMe() throws Exception
    {
        // Execute the tested code that modify the database here
        ...


        // Fetch database data after executing your code
        IDataSet databaseDataSet = getConnection().createDataSet();
        ITable actualTable = databaseDataSet.getTable("TABLE_NAME");

        // Load expected data from an XML dataset
        IDataSet expectedDataSet = new FlatXmlDataSetBuilder().build(new File("expectedDataSet.xml"));
        ITable expectedTable = expectedDataSet.getTable("TABLE_NAME");

        // Assert actual database table match expected table
        Assertion.assertEquals(expectedTable, actualTable);
    }
}

 

实际数据集是一个你要与预期的数据集进行比较的数据库快照。正如其名称表明,预期数据集包含的期望数据值。

预期的数据集必须与你用来设置你的数据库的数据集不同。因此,你需要两个数据集,一在测试之前设置数据库,另一个是在测试过程中提供预期的数据。

使用查询获取数据库快照

你也可以验证一个查询的结果是否与预期的数据集相等。查询可以用来查询单个表的一个子集或多个表的级联查询。

ITable actualJoinData = getConnection().createQueryTable("RESULT_NAME",
                "SELECT * FROM TABLE1, TABLE2 WHERE ..."); ;

 

在比较中忽略一些列

有时需要忽略一些列来进行比较;特别是主键,日期或时间值等根据测试代码生成的字段。一个解决方法是在期望的数据表中声明不需要的字段。然后,您可以筛选实际的数据库表只暴露预期的字段。
下面的代码片断显示了如何筛选实际的数据表。为了能够运行,实际的表必须至少包含所有预期表中的字段列。实际表中可以包含期望表中没有的额外字段。

        
ITable filteredTable = DefaultColumnFilter.includedColumnsTable(actual, 
            expected.getTableMetaData().getColumns());
    Assertion.assertEquals(expected, filteredTable); 

 

该技术的一个主要限制是你不能使用你的预期持平的XML数据集的DTD。有了你需要过滤的DTD列从预期的和实际的表。参见常见问题解答有关在运行时不包括一些表列

排序

默认情况下,DBUnit是按照主键排序来对获取数据库表的快照的。如果表没有主键或主键是数据库自动生成的,行的顺序是无法预测的,此时assertEquals将失败。

必须通过使用ORDER BY条件手动设置IDatabaseConnection.createQueryTable来获取排序的数据库表快照或者可以使用SortedTable装饰类:

Assertion.assertEquals(new SortedTable(expected),
                new SortedTable(actual, expected.getTableMetaData()));      

请注意,默认情况下SortedTable使用每一列的字符串值来排序因此,如果排序的是数字类型,排列顺序应该是如1,10,11,12,2,3,4。如果您想使用列的数据类型进行排序,如1,2,3,4,10,12),,可以如下操作:

SortedTable sortedTable1 = new SortedTable(table1, new String[]{"COLUMN1"});
          sortedTable1.setUseComparable(true); // must be invoked immediately after the constructor
          SortedTable sortedTable2 = new SortedTable(table2, new String[]{"COLUMN1"});
          sortedTable2.setUseComparable(true); // must be invoked immediately after the constructor
          Assertion.assertEquals(sortedTable1, sortedTable2);

参数目前不放在构造方法中原因是这样会使SortedTable的构造方法由4个变为8个,太多了。未来还需不断探索如何能更好地实现该功能

 

断言和收集差异

默认情况下,DBUnit发现第一个不一致数据时立即失败从DBunit2.4开始,可以通过注册一个定制的FailHandler来指定什么样的异常会被抛出,以及发现数据不一致时如何处理。使用DiffCollectingFailureHandler可以避免数据不匹配是抛出异常,使你可以在事后比对所有的结果集。

 

IDataSet dataSet = getDataSet();
DiffCollectingFailureHandler myHandler = new DiffCollectingFailureHandler();
//invoke the assertion with the custom handler
assertion.assertEquals(dataSet.getTable("TEST_TABLE"),
                       dataSet.getTable("TEST_TABLE_WITH_WRONG_VALUE"),
                       myHandler);
// Evaluate the results and throw an failure if you wish
List diffList = myHandler.getDiffList();
Difference diff = (Difference)diffList.get(0);

 



blog comments powered by Disqus