在很多成功的軟件項(xiàng)目中,測(cè)試自動(dòng)化往往是關(guān)鍵的層面。DBUnit允許開(kāi)發(fā)人員在測(cè)試之前給目標(biāo)數(shù)據(jù)庫(kù)植入測(cè)試數(shù)據(jù),在測(cè)試完畢后,再將數(shù)據(jù)庫(kù)恢復(fù)到測(cè)試前的狀態(tài)。在近的一個(gè)項(xiàng)目中,我嘗試使用用DBUnit對(duì)Spring+iBatis的架構(gòu)進(jìn)行測(cè)試,下面記錄了DBUnit的使用過(guò)程和遇到的一些問(wèn)題。
	測(cè)試環(huán)境
	首先,我們建立一個(gè)測(cè)試環(huán)境(基于Maven 2和Oracle數(shù)據(jù)庫(kù)*)。數(shù)據(jù)表名Account。
	數(shù)據(jù)庫(kù)
	先建立一個(gè)測(cè)試數(shù)據(jù)表(數(shù)據(jù)庫(kù)為Oracle*)
	Account.sql
	CREATE TABLE Account
	("ID" NUMBER,
	  "USERNAME" VARCHAR2(256 BYTE) NOT NULL ENABLE,
	  "PASSWORD" VARCHAR2(256 BYTE),
	    CONSTRAINT "ACCOUNT_UK_ID" UNIQUE ("ID"),
	    CONSTRAINT "ACCOUNT_PK" PRIMARY KEY ("USERNAME")
	)
這里我暫時(shí)不想涉及Sequence,所以主鍵**是username,而不是ID,并且ID允許為NULL。這是因?yàn)镾equence的遞增是不可恢復(fù)的,如果項(xiàng)目對(duì)記錄ID是否連續(xù)不是特別在意的話,可以在自己的項(xiàng)目中建立,只要稍微修改一下iBatis配置文件中的SQL語(yǔ)句可以了。這里我們先屏蔽這個(gè)問(wèn)題。
* DBUnit測(cè)試Oracle數(shù)據(jù)庫(kù)時(shí),帳戶(hù)好不要擁有DBA權(quán)限,否則會(huì)出現(xiàn)org.dbunit.database.AmbiguousTableNameException: COUNTRIES 錯(cuò)誤。如果帳戶(hù)必須具備DBA權(quán)限,那么需要在執(zhí)行new DatabaseConnection時(shí),明確給定SCHEMA(名稱(chēng)必須大寫(xiě)),詳細(xì)說(shuō)明參考下文多處代碼注釋和“org.dbunit.database.AmbiguousTableNameException異常”章節(jié)。
** 表必須存在主鍵,否則返回org.dbunit.dataset.NoPrimaryKeyException錯(cuò)誤。
	Spring配置文件
	ApplicationContext.xml
	<?xml version="1.0" encoding="UTF-8"?>
	<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
	   "http://www.springframework.org/dtd/spring-beans.dtd">
	 
	<beans>
	   <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	       <property name="locations">
	           <list>
	               <value>classpath:database.properties</value>
	           </list>
	       </property>
	   </bean>
	   <bean id="dataSource"
	      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	      <property name="driverClassName" value="${database.connection.driver_class}"/>
	      <property name="url" value="${database.connection.url}"/>
	      <property name="username" value="${database.connection.username}"/>
	      <property name="password" value="${database.connection.password}"/>
	   </bean>
	   <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
	      <property name="configLocation">
	          <value>SqlMapConfig.xml</value>
	      </property>
	      <property name="dataSource" ref="dataSource"/>
	   </bean>
	 
	   <bean id="accountManager" class="com.wang.dbunit.AccountManager">
	      <property name="sqlMapClient" ref="sqlMapClient"/>
	   </bean>
	</beans>
	database.properties
	database.connection.driver_class=oracle.jdbc.driver.OracleDriver
	database.connection.url=jdbc:oracle:thin:@xxx.xxx.xxx.xxx:1521:test
	database.connection.username=username
	database.connection.password=password
	iBatis配置文件
	SqlMapConfig.xml
	<?xml version="1.0" encoding="UTF-8"?>
	<!DOCTYPE sqlMapConfig
	   PUBLIC"-//iBATIS.com//DTD SQL Map Config 2.0//EN"
	   "http://www.ibatis.com/dtd/sql-map-config-2.dtd">
	<sqlMapConfig>
	   <settings
	      useStatementNamespaces="false"
	      cacheModelsEnabled="true"
	      enhancementEnabled="true"
	      lazyLoadingEnabled="true"
	      maxRequests="32"
	      maxSessions="10"
	      maxTransactions="5"
	   />
	   <sqlMap resource="Account.xml"/>
	</sqlMapConfig>
	Account.xml
	<?xml version="1.0" encoding="UTF-8"?>
	<!DOCTYPEsqlMap
	   PUBLIC"-//iBATIS.com//DTD SQL Map 2.0//EN"
	   "http://www.ibatis.com/dtd/sql-map-2.dtd">
	<sqlMap namespace="Account">
	   <resultMap id="accountMap" class="com.wang.dbunit.Account">
	      <result property="id" column="id" jdbcType="NUMBER" nullValue="0"/>
	      <result property="userName" column="username" jdbcType="VARCHAR2"/>
	       <result property="password" column="password" jdbcType="VARCHAR2"/>
	   </resultMap>
	   <!--** preserve ************************************** -->
	   <sql id="id-select">
	      <![CDATA[
	       SELECT id_sequence.nextval AS id FROM dual
	      ]]>
	   </sql>
	   <!--*************************************************** -->
	   <sql id="account-select">
	      <![CDATA[
	       SELECTid, username
	      ]]>
	      <dynamic prepend=",">
	          <isEqual
	           property="includePassword"
	           compareValue="true">
	            password
	          </isEqual>
	      </dynamic>
	      FROMaccount
	   </sql>
	   <sql id="account-where">
	      <![CDATA[
	       username=#userName:VARCHAR2#
	      ]]>
	      <dynamic>
	          <isNotNull
	           property="password"
	           prepend="AND ">
	             <![CDATA[
	              password=#password:VARCHAR2#
	             ]]>
	          </isNotNull>
	      </dynamic>
	   </sql>
	   <select id="getAccount"
	    parameterClass="com.wang.dbunit.Account"
	    resultMap="accountMap">
	      <include refid="account-select"/>
	      <dynamic prepend=" WHERE">
	          <isNotNull
	           property="userName">
	             <include refid="account-where"/>
	          </isNotNull>
	      </dynamic>
	   </select>
	   <!--**************************************************** -->
	   <sql id="account-insert">
	      <![CDATA[
	       INSERT INTO account(username, password
	      ]]>
	      <dynamic prepend=",">
	          <isNotEqual
	           property="id"
	           compareValue="0">
	          <![CDATA[
	            id
	         ]]>
	          </isNotEqual>
	      </dynamic>
	      )
	   </sql>
	 
	   <sql id="account-insert-values">
	      <![CDATA[
	       VALUES(#userName:VARCHAR2#, #password:VARCHAR2#
	      ]]>
	      <dynamic prepend=",">
	          <isNotEqual
	           property="id"
	           compareValue="0">
	          <![CDATA[
	            #id:NUMBER#
	         ]]>
	          </isNotEqual>
	      </dynamic>
	      )
	   </sql>
	   <insert id="createAccount"
	    parameterClass="com.wang.dbunit.Account">
	      <isEqual
	       property="generateIdFromSequence"
	       compareValue="true">
	          <include refid="id-select"/>
	      </isEqual>
	      <include refid="account-insert"/>
	      <include refid="account-insert-values"/>
	   </insert>
	</sqlMap>