在很多成功的软件项目中,测试自动化往往是关键的层面。DBUnit允许开发人员在测试之前给目标数据库植入测试数据,在测试完毕后,再将数据库恢复到测试前的状态。在近的一个项目中,我尝试使用用DBUnit对Spring+iBatis的架构进行测试,下面记录了DBUnit的使用过程和遇到的一些问题。
测试环境
首先,我们建立一个测试环境(基于Maven 2和Oracle数据库*)。数据表名Account。
数据库
先建立一个测试数据表(数据库为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")
)
这里我暂时不想涉及Sequence,所以主键**是username,而不是ID,并且ID允许为NULL。这是因为Sequence的递增是不可恢复的,如果项目对记录ID是否连续不是特别在意的话,可以在自己的项目中建立,只要稍微修改一下iBatis配置文件中的SQL语句可以了。这里我们先屏蔽这个问题。
* DBUnit测试Oracle数据库时,帐户好不要拥有DBA权限,否则会出现org.dbunit.database.AmbiguousTableNameException: COUNTRIES 错误。如果帐户必须具备DBA权限,那么需要在执行new DatabaseConnection时,明确给定SCHEMA(名称必须大写),详细说明参考下文多处代码注释和“org.dbunit.database.AmbiguousTableNameException异常”章节。
** 表必须存在主键,否则返回org.dbunit.dataset.NoPrimaryKeyException错误。
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>