接 續 資 料庫單元測試 的內容,自行撰寫測試用的輔助類別過於繁瑣且存在信任問題,對於類似的需求,你可以使用 DbUnit。假設你的資料表格如下建立:
create table T_BOOKMARK (
id int not null auto_increment primary key,
url varchar(255) not null,
title char(255) not null,
category char(255) not null
)
      
      id int not null auto_increment primary key,
url varchar(255) not null,
title char(255) not null,
category char(255) not null
)
你可以建立表格的初始資料集合,每次測試前由DbUnit讀入並建立在表格中,初始資料集合使用XML建立。例如建立dataset.xml:
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
   <t_bookmark id="1" url="http://a" title="b" category="c"/>
   <t_bookmark id="2" url="http://x" title="y" category="z"/>
</dataset>
<b_bookmark>表 示資料是在T_BOOKMARK表格中,當中的id、url、title與 category屬性分別代表資料將安插至T_BOOKMAR的id、url、title與category欄 位。由於你也會測試資料表格變更後的狀況,所以可以再建立各種預期資料集合。例如建立expected.xml:
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
   <t_bookmark id="1" url="http://a" title="b" category="c"/>
   <t_bookmark id="2" url="http://x" title="y" category="z"/>
   <t_bookmark id="3" url="http://m" title="n" category="o"/>
</dataset>
接下來可以如下建立測試類別,相關說明列於註解中:
package test.cc.openhome;
import static org.junit.Assert.assertEquals;
import static org.dbunit.Assertion.assertEqualsIgnoreCols;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.ext.mysql.MySqlConnection;
import org.dbunit.operation.DatabaseOperation;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import org.junit.Test;
import cc.openhome.dao.BookmarkDAO;
import cc.openhome.dao.BookmarkDAOImpl;
import cc.openhome.model.Bookmark;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
public class BookmarkDAOImplTest {
    private static MysqlDataSource dataSource;
    // DbUnit 用來與資料庫連線
    private static IDatabaseConnection dbConn;
    
    private BookmarkDAO dao;
    
    @BeforeClass
    public static void setUpClass() throws Exception {
        dataSource = new MysqlDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/exercise");
        dataSource.setUser("root");
        dataSource.setPassword("123456");
        dbConn = new MySqlConnection(dataSource.getConnection(), null);
    }
    @AfterClass
    public static void tearDownClass() throws Exception {
        dbConn.close();
    }
    // 讀取指定的資料集合
    private static IDataSet getXMLDataSet(String file) throws Exception {
        return new FlatXmlDataSetBuilder()
                       .build(new FileInputStream(file));
    }
    @Before
    public void setUp() throws Exception {
        // 測試前讀入初始資料集合、清除表格並將初始資料集合新增至資料庫中的表格
        DatabaseOperation.CLEAN_INSERT.execute(
                dbConn, getXMLDataSet("dataset.xml"));
        dao = new BookmarkDAOImpl(dataSource);
    }
    
    @Test
    public void testGet() throws Exception {
        // 用待測的DAO讀取資料
        List<Bookmark> result = dao.get();
        // 用 DbUnit 讀取資料
        IDataSet dataSet = dbConn.createDataSet();
        // 取得表格資料
        ITable table = dataSet.getTable("T_BOOKMARK");
        List<Bookmark> expected = new ArrayList<Bookmark>();
        // 表格中的列(row)數
        int rows = table.getRowCount();
        for(int i = 0; i < rows; i++) {
            // 取得每列的各個欄位
            String url = (String) table.getValue(i, "url");
            String title = (String) table.getValue(i, "title");
            String category = (String) table.getValue(i, "category");
            expected.add(new Bookmark(url, title, category));
        }
        // 斷言相等性
        assertEquals(expected, result);
    }
    
    @Test
    public void testAdd() throws Exception {
        // 用待測的DAO安插資料
        Bookmark bookmark = new Bookmark("http://m", "n", "o");
        dao.add(bookmark);
        // 用 DbUnit 取得資料
        IDataSet dataSet = dbConn.createDataSet();
        // 讀取預期資料集合
        IDataSet expected = getXMLDataSet("expected.xml");
        // 斷言資料集合相等性,但忽略id欄位
        assertEqualsIgnoreCols(expected, dataSet, 
                "T_BOOKMARK", new String[] {"id"});
    }
}
在這邊指定 DatabaseOperation的操作為CLEAN_INSERT,表示清空資料並重新安插指定的資料集合,為DELETE_ALL及INSERT兩 個動作的結合。你可以在這邊找到相關操作的說明:
在斷言的部份,DbUnit提供org.dbunit.Assertion類 別,其中assertEquals()方 法可直接斷言資料集合相等性,由於T_BOOKMARK的id主鍵採用自動遞增,所以在這邊測試時 想忽略id欄位的判別,因此採用org.dbunit.Assertion的assertEqualsIgnoreCols()指 定忽略id欄位。
上面的測試類別中,分別使用IDatabaseConnection、 DatabaseOperation等API,你可以使用IDatabaseTester的實作類別來集中進行這些操作。 IDatabaseTester提供了幾個實作類別:
- JdbcDatabaseTester: 使用DriverManager建立連線
- PropertiesBasedJdbcDatabaseTester: 讀取系統屬性,使用DriverManager建立連線
- DataSourceDatabaseTester: 使用DataSource建立連線
- JndiDatabaseTester: 使用JNDI取 得的DataSource建立連線
例如上例可以改用DataSourceDatabaseTester:
package test.cc.openhome;
import static org.junit.Assert.assertEquals;
import static org.dbunit.Assertion.assertEqualsIgnoreCols;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import org.dbunit.DataSourceDatabaseTester;
import org.dbunit.IDatabaseTester;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.operation.DatabaseOperation;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import cc.openhome.dao.BookmarkDAO;
import cc.openhome.dao.BookmarkDAOImpl;
import cc.openhome.model.Bookmark;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
public class BookmarkDAOImplTest {
    private static MysqlDataSource dataSource;
    
    private static IDatabaseTester databaseTester;
    
    private BookmarkDAO dao;
    
    @BeforeClass
    public static void setUpClass() throws Exception {
        dataSource = new MysqlDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/exercise");
        dataSource.setUser("root");
        dataSource.setPassword("123456");
        
        databaseTester = new DataSourceDatabaseTester(dataSource);
        databaseTester.setDataSet(getXMLDataSet("dataset.xml"));
        // 讀入初始資料集合、清除表格並將初始資料集合新增至資料庫中的表格(預設)
        //databaseTester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT);
    }
    // 讀取指定的資料集合
    private static IDataSet getXMLDataSet(String file) throws Exception {
        return new FlatXmlDataSetBuilder()
                       .build(new FileInputStream(file));
    }
    @Before
    public void setUp() throws Exception {
        databaseTester.onSetup(); 
        dao = new BookmarkDAOImpl(dataSource);
    }
    
    @Test
    public void testGet() throws Exception {
        // 用待測的DAO讀取資料
        List<Bookmark> result = dao.get();
        // 用 DbUnit 讀取資料
        IDataSet dataSet = databaseTester.getConnection().createDataSet();
        // 取得表格資料
        ITable table = dataSet.getTable("T_BOOKMARK");
        List<Bookmark> expected = new ArrayList<Bookmark>();
        // 表格中的列(row)數
        int rows = table.getRowCount();
        for(int i = 0; i < rows; i++) {
            // 取得每列的各個欄位
            String url = (String) table.getValue(i, "url");
            String title = (String) table.getValue(i, "title");
            String category = (String) table.getValue(i, "category");
            expected.add(new Bookmark(url, title, category));
        }
        // 斷言相等性
        assertEquals(expected, result);
    }
    
    @Test
    public void testAdd() throws Exception {
        // 用待測的DAO安插資料
        Bookmark bookmark = new Bookmark("http://m", "n", "o");
        dao.add(bookmark);
        // 用 DbUnit 取得資料
        IDataSet dataSet = databaseTester.getConnection().createDataSet();
        // 讀取預期資料集合
        IDataSet expected = getXMLDataSet("expected.xml");
        // 斷言資料集合相等性,但忽略id欄位
        assertEqualsIgnoreCols(expected, dataSet, 
                "T_BOOKMARK", new String[] {"id"});
    }
}
IDatabaseTester 的setSetUpOperation()可指定初始時的操作,onSetup()會執行指定的初始操作,setTearDownOperation()則可指定結束時的操作,onTearDown()則可以執行指定的結束操作。

