在很多成功的軟件項(xiàng)目中,測(cè)試自動(dòng)化往往是關(guān)鍵的層面。DBUnit允許開發(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的使用過程和遇到的一些問題。
測(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è)問題。
* DBUnit測(cè)試Oracle數(shù)據(jù)庫(kù)時(shí),帳戶好不要擁有DBA權(quán)限,否則會(huì)出現(xiàn)org.dbunit.database.AmbiguousTableNameException: COUNTRIES 錯(cuò)誤。如果帳戶必須具備DBA權(quán)限,那么需要在執(zhí)行new DatabaseConnection時(shí),明確給定SCHEMA(名稱必須大寫),詳細(xì)說明參考下文多處代碼注釋和“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>