MicroFish

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

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

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

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

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

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

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

          準備測試數據
          首先,要為單元測試準備數據。使用DbUnit,我們可以用XML文件來準備測試數據集。下面的XML文件稱為目標數據庫的Seed File,代表目標數據庫的表名和數據,它為測試準備了兩個Employee的數據。employee對應數據庫的表名,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中所有表的數據,然后導入Seed File的測試數據。在Seed File中不存在的表,DbUnit則不處理。
          Seed File可以手工編寫,也可以用程序導出現有的數據庫數據并生成。

          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接口來查詢已刪除的數據。因為使用SqlMapClient.queryForObject方法查詢,已刪除的數據還存在,真奇怪(有時間再研究)。

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

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

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

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

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

          使用Ant
          上面的方法通過extends DbUnit的DatabaseTestCase來控制數據庫的狀態。而
          使用DbUnit的Ant Task,完全可以通過Ant腳本的方式來實現。

          <taskdef name="dbunit" classname="org.dbunit.ant.DbUnitTask"/>
          <!-- 執行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>
          <!-- 執行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中間,可以達到控制數據庫狀態的目標。

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

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

          網上資源

          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 劉璐 閱讀(740) 評論(0)  編輯  收藏 所屬分類: other

          主站蜘蛛池模板: 遂川县| 崇信县| 巴南区| 佛教| 当雄县| 汾阳市| 荥阳市| 城步| 饶阳县| 河南省| 邛崃市| 石林| 吉首市| 祥云县| 左贡县| 弥渡县| 临高县| 平远县| 福鼎市| 武乡县| 忻州市| 南投市| 玛多县| 色达县| 商城县| 阜城县| 佛冈县| 庄河市| 曲水县| 东阳市| 乌海市| 荔波县| 阿拉善左旗| 平遥县| 博罗县| 赤水市| 曲麻莱县| 舒兰市| 四子王旗| 山西省| 金乡县|