<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    MicroFish

    Open & Open hits
    隨筆 - 33, 文章 - 2, 評論 - 4, 引用 - 0
    數(shù)據(jù)加載中……

    (轉(zhuǎn))用DbUnit進行SqlMap單元測試- -

    http://starrynight.blogdriver.com/starrynight/621943.html
    DbUnit簡介

    為依賴于其他外部系統(tǒng)(如數(shù)據(jù)庫或其他接口)的代碼編寫單元測試是一件很困難的工作。在這種情況下,有效的單元必須隔離測試對象和外部依賴,以便管理測試對象的狀態(tài)和行為。

    使用mock object對象,是隔離外部依賴的一個有效方法。如果我們的測試對象是依賴于DAO的代碼,mock object技術(shù)很方便。但如果測試對象變成了DAO本身,又如何進行單元測試呢?

    開源的DbUnit項目,為以上的問題提供了一個相當優(yōu)雅的解決方案。使用DbUnit,開發(fā)人員可以控制測試數(shù)據(jù)庫的狀態(tài)。進行一個DAO單元測試之前,DbUnit為數(shù)據(jù)庫準備好初始化數(shù)據(jù);而在測試結(jié)束時,DbUnit會把數(shù)據(jù)庫狀態(tài)恢復到測試前的狀態(tài)。

    下面的例子使用DbUnit為iBATIS SqlMap的DAO編寫單元測試。

    準備測試數(shù)據(jù)
    首先,要為單元測試準備數(shù)據(jù)。使用DbUnit,我們可以用XML文件來準備測試數(shù)據(jù)集。下面的XML文件稱為目標數(shù)據(jù)庫的Seed File,代表目標數(shù)據(jù)庫的表名和數(shù)據(jù),它為測試準備了兩個Employee的數(shù)據(jù)。employee對應數(shù)據(jù)庫的表名,employee_uid、start_date、first_name和last_name都是表employee的列名。

    <?xml version="1.0" encoding="GB2312"?>
    <dataset>
    ??? <employee employee_uid="0001"
    ??? ??? start_date="2001-01-01"
    ??? ??? first_name="liutao"
    ??? ??? last_name="liutao" />
    ???
    ??? <employee employee_uid="0002"
    ??? ??? start_date="2001-04-01"
    ??? ??? first_name="wangchuang"
    ??? ??? last_name="wangchuang" />
    </dataset>

    缺省情況下,DbUnit在單元測試開始之前刪除Seed File中所有表的數(shù)據(jù),然后導入Seed File的測試數(shù)據(jù)。在Seed File中不存在的表,DbUnit則不處理。
    Seed File可以手工編寫,也可以用程序?qū)С霈F(xiàn)有的數(shù)據(jù)庫數(shù)據(jù)并生成。

    SqlMap代碼
    我們要測試的SqlMap映射文件如下所示:
    <select id="queryEmployeeById" parameterClass="java.lang.String"
    ??? resultClass="domain.Employee">
    ??? select employee_uid as userId,
    ??? ??? start_date as startDate,
    ??? ??? first_name as firstName,
    ??? ??? last_name as lastName
    ??? from EMPLOYEE where employee_uid=#value#
    </select>
    <delete id="removeEmployeeById" parameterClass="java.lang.String">
    ??? delete from EMPLOYEE where employee_uid=#value#
    </delete>
    <update id="updateEmpoyee" parameterClass="domain.Employee">
    ??? update EMPLOYEE
    ??? set start_date=#startDate#,
    ??? first_name=#firstName#,
    ??? last_name=#lastName#
    ??? where employee_uid=#userId#
    </update>
    <insert id="insertEmployee" parameterClass="domain.Employee">
    ??? insert into employee (employee_uid,
    ??? ??? start_date, first_name, last_name)
    ??? ??? values (#userId#, #startDate#, #firstName#, #lastName#)
    </insert>

    編寫DbUnit TestCase
    為了方便測試,首先為SqlMap的單元測試編寫一個抽象的測試基類,代碼如下。

    public abstract class BaseSqlMapTest extends DatabaseTestCase {
    ??? protected static SqlMapClient sqlMap;

    ??? protected IDatabaseConnection getConnection() throws Exception {
    ??? ??? return new DatabaseConnection(getJdbcConnection());
    ??? }
    ??? protected void setUp() throws Exception {
    ??? ??? super.setUp();
    ??? ??? init();
    ??? }
    ??? protected void tearDown() throws Exception {
    ??? ??? super.tearDown();
    ??? ??? getConnection().close();
    ??? ??? if (sqlMap != null) {
    ??? ??? ??? DataSource ds = sqlMap.getDataSource();
    ??? ??? ??? Connection conn = ds.getConnection();
    ??? ??? ??? conn.close();
    ??? ??? }
    ??? }
    ??? protected void init() throws Exception {
    ??? ??? initSqlMap("sqlmap/SqlMapConfig.xml", null);
    ??? }
    ??? protected SqlMapClient getSqlMapClient() {
    ??? ??? return sqlMap;
    ??? }
    ??? protected void initSqlMap(String configFile, Properties props)
    ??? ??? ??? throws Exception {
    ??? ??? Reader reader = Resources.getResourceAsReader(configFile);
    ??? ??? sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader, props);
    ??? ??? reader.close();
    ??? }
    ??? protected void initScript(String script) throws Exception {
    ??? ??? DataSource ds = sqlMap.getDataSource();
    ??? ??? Connection conn = ds.getConnection();
    ??? ???
    ??? ??? Reader reader = Resources.getResourceAsReader(script);
    ??? ??? ScriptRunner runner = new ScriptRunner();
    ??? ??? runner.setStopOnError(false);
    ??? ??? runner.setLogWriter(null);
    ??? ??? runner.setErrorLogWriter(null);

    ??? ??? runner.runScript(conn, reader);
    ??? ??? conn.commit();
    ??? ??? conn.close();
    ??? ??? reader.close();
    ??? }
    ??? private Connection getJdbcConnection() throws Exception {
    ??? ??? Properties props = new Properties();
    ??? ??? props.load(Resources.getResourceAsStream("sqlmap/SqlMapConfig.properties"));
    ??? ??? Class driver = Class.forName(props.getProperty("driver"));
    ??? ??? Connection conn = DriverManager.getConnection(props.getProperty("url"),
    ??? ??? ??? ??? props.getProperty("username"), props.getProperty("password"));
    ??? ??? return conn;
    ??? }
    }

    然后為每個SqlMap映射文件編寫一個測試用例,extends上面的抽象類。如編寫Employ.xml的測試用例如下,它覆蓋了DbUnit的DatabaseTestCase類的getDataSet方法。

    public class EmployeeDaoTest extends BaseSqlMapTest {
    ???
    ??? protected IDataSet getDataSet() throws Exception {
    ??? ??? Reader reader = Resources.getResourceAsReader("config/employee_seed.xml");
    ??? ??? return new FlatXmlDataSet(reader);
    ??? }
    ??? public void testQueryEmpoyeeById() throws Exception {
    ??? ??? String id = "0001";
    ??? ??? Employee emp = (Employee)sqlMap.queryForObject("queryEmployeeById", id);
    ??? ??? assertNotNull(emp);
    ??? ??? assertEquals("0001", emp.getUserId());
    ??? ??? assertEquals("liutao", emp.getFirstName());
    ??? }
    ??? public void testRemoveEmployeeById() throws Exception {
    ??? ??? String id = "0001";
    ??? ??? int num = sqlMap.delete("removeEmployeeById", id);
    ??? ??? assertEquals(1, num);
    ??? ???
    ??? ??? // 注意這里, 確認刪除不能使用SqlMap的查詢, 很奇怪!
    ??? ??? ITable table = getConnection().createQueryTable("removed",
    ??? ??? ??? ??? "select * from employee where employee_uid='0001'");
    ??? ??? assertEquals(0, table.getRowCount());
    ??? }
    ??? public void testUpdateEmployee() throws Exception {
    ??? ??? String id = "0002";
    ??? ??? Employee emp = (Employee)sqlMap.queryForObject("queryEmployeeById", id);
    ??? ??? emp.setLastName("wch");
    ??? ??? sqlMap.update("updateEmpoyee", emp);
    ??? ???
    ??? ??? Employee emp1 = (Employee)sqlMap.queryForObject("queryEmployeeById", id);
    ??? ??? assertEquals("wch", emp1.getLastName());
    ??? }
    ??? public void testInsertEmployee() throws Exception {
    ??? ??? Employee emp = new Employee();
    ??? ??? emp.setUserId("0005");
    ??? ??? emp.setStartDate("2003-09-09");
    ??? ??? emp.setFirstName("macy");
    ??? ??? emp.setLastName("macy");
    ??? ??? sqlMap.insert("insertEmployee", emp);
    ??? ???
    ??? ??? Employee emp1 = (Employee)sqlMap.queryForObject("queryEmployeeById", "0005");
    ??? ??? assertEquals(emp.getFirstName(), emp1.getFirstName());
    ??? ??? assertEquals(emp.getStartDate(), emp1.getStartDate());
    ??? }
    }

    以上例子中的綠色代碼部分使用ITable接口來查詢已刪除的數(shù)據(jù)。因為使用SqlMapClient.queryForObject方法查詢,已刪除的數(shù)據(jù)還存在,真奇怪(有時間再研究)。

    DbUnit的斷言
    我們可以使用DbUnit的Assertion類的方法來比較數(shù)據(jù)是否相同。

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

    DatabaseTestCase的getSetUpOperation和getTearDownOperation方法
    缺省情況下,DbUnit執(zhí)行每個測試前,都會執(zhí)行CLEAN_INSERT操作,刪除Seed File中所有表的數(shù)據(jù),并插入文件的測試數(shù)據(jù)。你可以通過覆蓋getSetUpOperation和getTearDownOperation方法改變setUp和tearDown的行為。

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

    REFRESH操作執(zhí)行測試前并不執(zhí)行CLEAN操作,只是導入文件中的數(shù)據(jù),如果目標數(shù)據(jù)庫數(shù)據(jù)已存在,DbUnit使用文件的數(shù)據(jù)來更新數(shù)據(jù)庫。

    使用Ant
    上面的方法通過extends DbUnit的DatabaseTestCase來控制數(shù)據(jù)庫的狀態(tài)。而
    使用DbUnit的Ant Task,完全可以通過Ant腳本的方式來實現(xiàn)。

    <taskdef name="dbunit" classname="org.dbunit.ant.DbUnitTask"/>
    <!-- 執(zhí)行set up 操作 -->
    <dbunit driver="org.hsqldb.jdbcDriver"
    ??????? url="jdbc:hsqldb:hsql://localhost/xdb"
    ??????? userid="sa" password="">
    ??? <operation type="INSERT" src="employee_seed.xml"/>
    </dbunit>
    <!-- run all tests in the source tree -->
    <junit printsummary="yes" haltonfailure="yes">
    ? <formatter type="xml"/>
    ? <batchtest fork="yes" todir="${reports.tests}">
    ??? <fileset dir="${src.tests}">
    ????? <include name="**/*Test*.java"/>
    ??? </fileset>
    ? </batchtest>
    </junit>
    <!-- 執(zhí)行tear down 操作 -->
    <dbunit driver="org.hsqldb.jdbcDriver"
    ??????? url="jdbc:hsqldb:hsql://localhost/xdb"
    ??????? userid="sa" password="">
    ??? <operation type="DELETE" src="employee_seed.xml"/>
    </dbunit>

    以上的Ant腳本把junit task放在DbUnit的Task中間,可以達到控制數(shù)據(jù)庫狀態(tài)的目標。

    由此可知,DbUnit可以靈活控制目標數(shù)據(jù)庫的測試狀態(tài),從而使編寫SqlMap單元測試變得更加輕松。

    本文抄襲了資源列表的“Effective Unit Test with DbUnit”,但重新編寫了代碼示例。

    網(wǎng)上資源

    1、DbUnit Framework

    2、Effective Unit Testing with DbUnit

    3、Control your test-environement with DbUnit and Anthill

    posted on 2007-03-26 17:16 劉璐 閱讀(733) 評論(0)  編輯  收藏 所屬分類: other

    主站蜘蛛池模板: 91精品国产免费入口| 国产成人精品久久亚洲| 2019中文字幕免费电影在线播放| 久久久亚洲欧洲日产国码aⅴ| 中文字幕免费在线看| 久久亚洲精品成人无码网站| 男女交性永久免费视频播放| 亚洲人配人种jizz| 1000部拍拍拍18勿入免费凤凰福利| 久久亚洲中文字幕精品有坂深雪 | 国产在线a不卡免费视频| 精选影视免费在线 | 亚洲国产人成在线观看| 日本亚洲国产一区二区三区| 久久国产精品免费一区二区三区| 亚洲第一网站免费视频| 亚洲精品国产综合久久一线| 美女网站免费福利视频| 国产精品青草视频免费播放| www.亚洲成在线| 久久精品国产亚洲AV网站| 四虎免费永久在线播放| 精品久久久久久无码免费| 亚洲色大成网站www久久九| 亚洲精品天天影视综合网 | 亚洲国产成人资源在线软件| 亚洲人成电影网站国产精品| 免费无码AV片在线观看软件| 亚洲aⅴ无码专区在线观看春色| 久久精品国产精品亚洲艾| 亚洲&#228;v永久无码精品天堂久久 | 亚洲视频中文字幕| 亚洲一区日韩高清中文字幕亚洲 | 99久久人妻精品免费二区| 一级毛片在线免费视频| 亚洲色大情网站www| 亚洲无成人网77777| 亚洲精品无码久久久影院相关影片| 国产免费卡一卡三卡乱码 | 亚洲电影国产一区| 亚洲日韩精品无码专区网址|