- 浏览: 414734 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
305954240:
好,好,好文。。。
facebook怎么赚钱?facebook盈利模式解析 -
天外鸭:
你好,我想问一些,那个runsall是哪个版本的命令,我在9. ...
db2常用命令大全 -
tterry:
这个叫热部署的话真是羞煞我等
idea -
Torero:
请求的不是Action的Execute方法, 而是其他方法呢? ...
struts2拦截器实现权限控制 -
fortaotao:
咨询一个问题,<security-constraint& ...
备忘:启用 Tomcat 下的 HTTPS
11月7日
Spring+iBatis+DBUnit测试详解
在很多成功的软件项目中,测试自动化往往是关键的层面。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>
(这个配置文件中预留了未来使用 sequence 的可能)
DBUnit配置文件
我们通过一个xml种子文件(seedfile)为DBUnit提供测试数据,文件中的数据会被DBUnit在测试开始前自动植入数据表,这个文件结构很简单:
dataSet.xml
<?xml version='1.0' encoding='UTF-8'?>
<DATASET>
<ACCOUNT id='1'
username='Drew'
password='Smith'/>
<ACCOUNT id='2'
username='Nick'
password='Marquiss'/>
<ACCOUNT id='3'
username='Jose'
password='Whitson'/>
</DATASET>
“ACCOUNT”就是表名称,它的属性就是字段内容。
代码
辅助类Accout.java
package com.wang.dbunit;
public class Account
{
private boolean generateIdFromSequence=false;
private boolean includePassword = false;
private long id = 0;
private String userName = null;
private String password = null;
public boolean getGenerateIdFromSequence()
{
return generateIdFromSequence;
}
public void setGenerateIdFromSequence(boolean generateIdFromSequence)
{
this.generateIdFromSequence =generateIdFromSequence;
}
public void setId(long id)
{
this.id =id;
}
public long getId()
{
return this.id;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password =password;
}
public String getUserName()
{
return userName;
}
public void setUserName(String userName)
{
this.userName =userName;
}
public boolean isIncludePassword()
{
return includePassword;
}
public void setIncludePassword(boolean includePassword)
{
this.includePassword =includePassword;
}
}
业务类AccountManager.java
package com.wang.dbunit;
import com.ibatis.sqlmap.client.SqlMapClient;
public class AccountManager
{
protected SqlMapClient sqlMap = null;
public void setSqlMapClient(SqlMapClient sqlMapClient)
{
this.sqlMap =sqlMapClient;
}
public Account getAccount(String userName, String password, boolean includePassword) throws Exception
{
Account account = new Account();
account.setUserName(userName);
account.setPassword(password);
account.setIncludePassword(includePassword);
Account ret
= (Account)(sqlMap.queryForObject("getAccount", account));
return ret;
}
public void createAccount(String userName, String password) throws Exception
{
Account account = new Account();
account.setUserName(userName);
account.setPassword(password);
sqlMap.insert("createAccount",account);
}
}
好了,我们完成了了全部测试环境,接下来我们要开始编写测试用例。
测试
DatabaseTestCase类
DBUnit提供了一个抽象类: DatabaseTestCase,它继承自 JUnit的 TestCase,这个类有两个方法需要重载:
protecte abstract IDatabaseConnection getConnection() throws Exception;
protected abstract IDataSet getDataSet() throws Exception;
getConnection用于告诉测试用例如何找到数据库连接;getDataSet用于告诉测试用例如何获取测试数据文件(dataSet.xml)。下面我们先继承这个抽象类编写测试用例:
package com.wang.dbunit;
import java.io.FileInputStream;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import com.wang.dbunit.Account;
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.dbunit.DatabaseTestCase;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.operation.DatabaseOperation;
public class HelloDBUnit extends DatabaseTestCase
{
static Logger logger
= LogManager.getLogger(HelloDBUnit.class.getName());
privatestatic ApplicationContext context;
protected Properties props = new Properties();
public HelloDBUnit() throws IOException
{
super();
props.load(Resources.getResourceAsStream(
"database.properties"));
context = newClassPathXmlApplicationContext(
"classpath:ApplicationContext.xml");
}
////////////////////////////////////////////////
@Override
protected IDatabaseConnection getConnection() throws Exception
{
DataSourcedataSource
= (DataSource)context.getBean("dataSource");
Connectionconnection = dataSource.getConnection();
// 如果所用测试帐户是 DBA,为了避免出现 AmbiguousTableNameException
// 异常,下面必须改写为 newDatabaseConnection(connection, SCHEMA)
// 形式。注意SCHEMA 要大写**
return new DatabaseConnection(connection);
}
@Override
protected IDataSet getDataSet()throws Exception
{
return new FlatXmlDataSet(
new FileInputStream("bin/dataSet.xml"));
}
///////////////////////////////////////////////
@Override
protected DatabaseOperation getSetUpOperation() throws Exception
{
return DatabaseOperation.REFRESH;
}
@Override
protected DatabaseOperation getTearDownOperation() throws Exception
{
return DatabaseOperation.NONE;
}
////////////////////////////////////////////////////
public void testSelectAccount()
{
AccountManager manager
= (AccountManager)context.getBean("accountManager");
try
{
Accountaccount
= manager.getAccount("Nick", "Marquiss", true);
assertNotNull(account);
}
catch (Exceptione)
{
logger.error(e.getMessage(),e);
}
}
public void testCreateAccount()
{
AccountManager manager
= (AccountManager)context.getBean("accountManager");
try
{
manager.createAccount("TEST", "test");
}
catch(Exception e)
{
logger.error(e.getMessage(),e);
}
}
}
在getConnection方法中,我们通过Spring配置文件获得数据连接。
除了前面那两个方法外,我们还重载了 getSetUpOperation 和 getTearDownOperation 方法:DatabaseOperation.REFRESH 告诉DBUnit在测试开始前比较数据库和配置文件,如果发现测试数据不存或不一致在则插入或更新***。DatabaseOperation.NONE表示什么也不做。
这个CASE应该可以运行的很好,但是如果我们把 getTearDownOperation改成:
@Override
protected DatabaseOperation getTearDownOperation() throws Exception
{
return DatabaseOperation.DELETE_ALL;
}
就会发生java.sql.SQLException: Closed Connection异常。这是为什么呢?问题出在DatabaseTestCase中。
***参数含义
DatabaseOperation.CLEAN_INSERT; 先删除表中所有,再插入准备的数据
DatabaseOperation.REFRESH; 使用准备数据更新表,存在则update,不存在则insert
DatabaseOperation.DELETE; 只删除准备的数据
DatabaseOperation.DELETE_ALL 清除所有记录
DatabaseOperation.NONE; 啥都不做
java.sql.SQLException: Closed Connection异常
来看一下DatabaseTestCase的一个关键成员变量tester和有关的一些方法:
public abstract class DatabaseTestCase extends TestCase
{
......
private IDatabaseTester tester;
......
protected IDatabaseTester getDatabaseTester() throws Exception {
if (this.tester == null) {
this.tester = newDatabaseTester();
}
return this.tester;
}
......
protected IDatabaseTester newDatabaseTester() throws Exception{
logger.debug("newDatabaseTester()- start");
// 重载的 getConnection 方法,在 IDatabaseTester 里有一个同名方法。
// 注意区分。
final IDatabaseConnection connection = getConnection();
final IDatabaseTester tester
= new DefaultDatabaseTester(connection);
return tester;
}
......
protected void setUp() throws Exception
{
logger.debug("setUp()- start");
super.setUp();
final IDatabaseTester databaseTester = getDatabaseTester();
assertNotNull("DatabaseTesteris not set", databaseTester);
databaseTester.setSetUpOperation(getSetUpOperation());
databaseTester.setDataSet(getDataSet());
databaseTester.onSetup();
}
......
}
可见 DatabaseTestCase 内部有一个 IDatabaseTester 接口的实例(tester),实际上所有的测试工作是由它完成的。而DatabaseTestCase的newDatabaseTester方法在生成这个实例的时候用的是DefaultDatabaseTester。传入一个由重载的getConnection方法返回的IDatabaseConnection实例。
DefaultDatabaseTester记录了这个连接实例后,提供了一个同名的getConnection()方法(不是DatabaseTestCase中被重载的那个getConnection),用来返回它:
public class DefaultDatabaseTester extends AbstractDatabaseTester
{
final IDatabaseConnection connection;
public DefaultDatabaseTester(final IDatabaseConnection connection){
this.connection= connection;
}
public IDatabaseConnection getConnection() throws Exception {
return this.connection;
}
}
因为所有的IDatabaseTester实现(包括DefaultDatabaseTester)都继承自AbatractDatabaseTester,这个抽象类有一个统一的执行数据库操作的方法executeOperation,原代码如下:
private void executeOperation(DatabaseOperation operation) throws Exception
{
logger.debug("executeOperation(operation={})- start", operation);
if(operation != DatabaseOperation.NONE ){
// IDatabaseTester 的 getConnection 方法,不是重载的那个。
IDatabaseConnection connection = getConnection();
try{
operation.execute(connection, getDataSet() );
}
finally{
closeConnection(connection);
}
}
}
我们看到每执行完一次操作,数据库连接都会被关闭,所以如果继承DefaultDatabaseTester,将导致只能执行一次数据库操作。
如果希望在一个TestCase里执行两次操作,我们可以使用另一个基类
DBTestCase类
如上面所看到的,问题出在DatabaseTestCase的newDatabaseTester方法返回了一个无法重复利用的DefaultDatabaseTester实例,所以DBTestCase的newDatabaseTester方法代码变更如下:
protected IDatabaseTester newDatabaseTester() throws Exception {
return new PropertiesBasedJdbcDatabaseTester();
}
它用来生成实例的是 PropertiesBasedJdbcDatabaseTester 类,而不是 DefaultDatabaseTester 。这个类的父类 JdbcDatabaseTester(也继承自 AbstractDatabaseTester)的 getConnection 方法:
public IDatabaseConnection getConnection() throws Exception
{
logger.debug("getConnection() - start");
if(!initialized ){
// 注意这个方法,等一下详解
initialize();
}
assertNotNullNorEmpty("connectionUrl", connectionUrl);
Connection conn = null;
if(username == null && password == null ){
conn = DriverManager.getConnection(connectionUrl);
}else{
Conn = DriverManager.getConnection(connectionUrl,username,password);
}
return new DatabaseConnection( conn, getSchema() );
}
可以看到每次调用这个方法,都会新建一个连接,而不是简单的返回我们重载的 getConnection 中返回的连接的引用。这样就避免了 DefaultDatabaseTester 仅仅是简单返回之前的连接而倒置的问题。不过这也意味着用 DBTestCase 就不用我们自己去重载 getConnection 了,因为 DBTestCase 已经实现了这个方法(DatabaseTestCase没有实现):
protected IDatabaseConnection getConnection() throws Exception {
logger.debug("getConnection() - start");
final IDatabaseTester databaseTester = getDatabaseTester();
assertNotNull( "DatabaseTester is not set",databaseTester);
return databaseTester.getConnection();
}
我们看到DBTestCase的getConnection简单的把这个方法转给JdbcDatabaseTester(IDatabaseTester) 的getConnection。而JdbcDatabaseTester的实现我们在前面已经看到了。
现在我们把DatabaseTestCase替换成DBTestCase,并且注释掉HelloDBUnit中重载的getConnection(不再需要了,父类DBTestCase中已经实现了该方法。)然后执行,我们又遇到一个异常:
org.dbunit.AbstractDatabaseTester$AssertionFailedError:driverClass is null
这是因为PropertiesBasedJdbcDatabaseTester的initialize方法(见上面代码)用来初始化数据库连接参数:
protected void initialize() throwsException
{
logger.debug("initialize() - start");
setDriverClass(System.getProperty(DRIVER_CLASS));
setConnectionUrl(System.getProperty(CONNECTION_URL));
setUsername(System.getProperty(USERNAME));
setPassword(System.getProperty(PASSWORD));
// 如果所用测试帐户是 DBA,为了避免出现 AmbiguousTableNameException
// 异常,必须明确给出 SCHEMA。注意 SCHEMA要大写**
// setSchema(System.getProperty(SCHEMA));
super.initialize();
}
从这里我们看到DBTestCase需要从系统变量中获得连接参数,所以我们必须修改HelloDBUnit的构造函数,将配置参数读入系统变量:
public HelloDBUnit() throws IOException
{
super();
props.load(Resources.getResourceAsStream("database.properties"));
if(null == context)
{
context = new ClassPathXmlApplicationContext(
"classpath:ApplicationContext.xml");
}
System.setProperty(
PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS,
props.getProperty("database.connection.driver_class"));
System.setProperty(
PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL,
props.getProperty("database.connection.url"));
System.setProperty(
PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME,
props.getProperty("database.connection.username"));
System.setProperty(
PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD,
props.getProperty("database.connection.password"));
}
现在可以正确执行了。不过,这依然存在遗憾,既然我们使所有配置参数都文本化了,就不希望看到
System.setProperty(
PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS,
props.getProperty("database.connection.driver_class"));
这样的代码,因为如果我们有多个数据源,或改变了配置文件,我们不得不还需要改变测试用例的代码。可以用DataSourceBasedDBTestCase 来解决这个问题。
DataSourceBasedDBTestCase类
DataSourceBasedDBTestCase 抽象类要求子类实现 getDataSource 方法以获取外部数据源。
首先,改变父类为 DataSourceBasedDBTestCase 。然后移除构造函数中关于系统变量的设置。最后加入以下代码:
@Override
protected DataSource getDataSource(){
return(DataSource)context.getBean("dataSource");
}
org.dbunit.database.AmbiguousTableNameException异常
** 很遗憾, DataSourceBasedDBTestCase 没有提供设置 SCHEMA 的方法,虽然它的成员变量 DataSourceBasedDBTester 通过继承了 AbstractDatabaseTester 而提供了 setSchema 方法,但是因为所有的 IDatabaseTester 都是 Private 变量,因此实际上我们无法访问到它的 setSchema。所以如果使用 DataSourceBasedDBTestCase ,除非重载 setUp 和tearDown方法,否则就不能有DBA权限。
protected void setUp() throws Exception
{
DataSource dataSource = getDataSource();
Connection connection = dataSource.getConnection();
IDatabaseConnection dbUnitCon
= new DatabaseConnection(connection, "SYSTEM");
if(getDataSet() != null)
{
try
{
getSetUpOperation().execute(dbUnitCon, getDataSet());
}
finally
{
if(connection!= null)
connection.close();
}
}
}
protected void tearDown() throws Exception
{
DataSource dataSource = getDataSource();
Connection connection = dataSource.getConnection();
IDatabaseConnection dbUnitCon
= new DatabaseConnection(connection, "SYSTEM");
if(getDataSet()!= null)
{
try
{
getTearDownOperation().execute(dbUnitCon,getDataSet());
}
finally
{
if(connection!= null)
connection.close();
}
}
}
支持事务回滚
虽然DBUnit提供了一些方法让我们可以在测试开始和结束时清理数据库,但是有时候依然不能满足需求,比如在上面的代码中,我们在执行阶段插入了一条记录(见testCreateAccount方法),这种不是在种子文件中的额外数据,测试结束后除非在tearDown中返回DatabaseOperation.DELETE_ALL,否则是不会被自动删除的,可是如果删除全部数据,那么又有可能删掉了不希望删掉的数据。Spring提供了一个AbstractTransactionalDataSourceSpringContextTests测试类,这个类可以在测试结束后回滚数据库,可是DBUnit没有提供类似的机制,所以我们要进一步手工扩展测试用例,以加入类似功能。
修改ApplicationContext.xml
首先,修改Spring的配置文件ApplicationContext.xml,加入以下配置:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
如果希望业务类也支持事务处理可以加入以下配置,否则可以略过:
<bean id="accountManagerTarget" class="com.wang.dbunit.AccountManager">
<property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
<bean id="accountManager" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>
com.wang.dbunit.IAccountManager
</value>
</property>
<property name="interceptorNames">
<list>
<idref local="transactionInterceptor" />
<idref local="accountManagerTarget" />
</list>
</property>
</bean>
以上配置只作用于使业务类,因为我们的测试用例类“HelloDBUnit.java”没有出现在配置文件中,更没有设置任何拦截器,所以测试用例对数据库的所有操作(插入、清除测试数据)目前都不在拦截范围。我们必须在测试用例中手工为它加入事务处理,才可以达到我们的目的。
添加事务管理代码
添加以下属性和方法:
private TransactionStatus ts = null;
private DataSourceTransactionManager transactionManager = null;
......
protected void setUpTransaction()
{
transactionManager
=(DataSourceTransactionManager)context.getBean(
"transactionManager");
TransactionDefinition td = new DefaultTransactionDefinition();
ts = transactionManager.getTransaction(td);
}
protected void tearDownTransaction(boolean commit)
{
if(commit)
{
transactionManager.commit(ts);
}
else
{
transactionManager.rollback(ts);
}
}
修改setUp和tearDown方法:
protected void setUp() throws Exception
{
setUpTransaction();
DataSourced ataSource = getDataSource();
// 替换 Connection connection = dataSource.getConnection();
Connection connection = DataSourceUtils.getConnection(dataSource);
IDatabaseConnection dbUnitCon
= new DatabaseConnection(connection, "SYSTEM");
if(getDataSet()!= null)
{
try
{
getSetUpOperation().execute(dbUnitCon, getDataSet());
}
finally
{
if(connection!= null)
{
// 替换 connection.close();
DataSourceUtils.releaseConnection(
connection, dataSource);
}
}
}
}
protected void tearDown() throws Exception
{
DataSource dataSource = getDataSource();
// 替换 Connection connection = dataSource.getConnection();
Connection connection = DataSourceUtils.getConnection(dataSource);
IDatabaseConnection dbUnitCon
= new DatabaseConnection(connection, "SYSTEM");
if(getDataSet() != null)
{
try
{
// 如果不希望回滚数据,传入 true 参数。
tearDownTransaction(false);
getTearDownOperation().execute(dbUnitCon, getDataSet());
}
finally
{
if(connection!= null)
{
// 替换 connection.close();
DataSourceUtils.releaseConnection(
connection, dataSource);
}
}
}
}
最后修改getTearDownOperation,用 DELETE 替换 DELETE_ALL:
protected DatabaseOperation getTearDownOperation() throws Exception
{
return DatabaseOperation.DELETE;
}
现在在表中随便添加一些记录,然后执行我们的测试用例,执行完后,手工添加的数据没有受到任何影响。
最后一点提示
因为每一个 testXxx 方法时都会初始化一个测试用例,所以每执行一次 testXxxx 方法都会导致 setUp 和 tearDown 方法被调用一次,而本例的事务定义也都由 setUp 开始, tearDown 结束,也就意味着不同测试方法(同一测试用例)之间处于不同的事务范围,导致数据操作结果无法共享(如果每次 tearDown 都回滚数据)。比如在 testInsert 方法中插入数据,在 testSelect 无法获得。要解决这个问题,就要适当的修改对 setUpTransaction 和 dearDownTransaction 的调用,使得事务可以在全局范围内被多个 test 方法共享。
E:\work\bpm2_v1_00\test.xml:171: org.dbunit.dataset.NoSuchTableException: VERSION_INFO
DBunit 要求schema 大写
相关推荐
SpringBoot DBUnit 单元测试详解 在软件开发过程中,单元测试是非常重要的一步骤。SpringBoot 提供了强大的单元测试工具,而 DBUnit 则是一种扩展于 JUnit 的数据库驱动测试框架。今天,我们将详细介绍 SpringBoot ...
**dbunit-mongodb 模块详解** 在 IT 领域,数据库测试是软件开发过程中不可或缺的一环。MongoDB 是一款流行的开源、分布式文档型数据库,而 DbUnit 是 Java 开发中的一个单元测试框架,主要用于数据库的数据填充和...
### Dbunit指南知识点详解 #### 一、Dbunit概述 Dbunit是一个强大的数据库测试框架,其核心功能在于能够简化数据库的测试过程,并确保测试环境的一致性和可预测性。Dbunit是基于JUnit扩展而成,因此它继承了JUnit...
"数据库测试详解" 数据库测试是软件测试中的一部分,随着数据库在软件系统中的重要性不断提高,数据库测试的重要性也日益凸显。数据库测试的目的是为了确保数据库的正确性、安全性和性能,以满足软件系统的需求。...
### 数据库测试知识点详解 #### 一、为什么需要进行数据库测试? 随着大数据时代的到来,数据库已经不再是简单的数据存储仓库,而是成为了系统的核心组成部分之一。因此,对于数据库的测试变得尤为重要。通过对...
在CRLServer的开发和测试过程中,DBUnit可以协助填充和清理数据库,确保测试环境的一致性和可重复性。通过使用DBUnit,开发者可以更专注于业务逻辑的验证,而不是繁琐的数据准备。 **4. Maven构建工具** Maven是...
- **第16章:使用DBUnit进行数据库测试**:讲解如何测试数据库操作的有效性。 - **第17章:测试基于JPA的应用程序**:探讨Java Persistence API (JPA) 在测试中的应用。 - **第18章:JUnit的高级应用**:介绍如何...
《测试驱动的Spring Boot应用程序详解》 在软件开发领域,测试驱动开发(Test-Driven Development,简称TDD)是一种编程实践,它强调先编写测试,然后编写满足这些测试的代码。这种模式有助于确保代码的质量,并且...
pear install pear.phpunit.de/DbUnit ``` 如果因为网络问题导致安装失败,可以多次尝试。安装完成后,可以通过运行 `phpunit` 命令来验证是否安装成功。 为了编写和执行 `phpunit` 测试,我们需要创建一个被测试...
使用JUnit和Mockito进行单元测试,通过Spring Test和DBUnit进行集成测试,确保代码的质量和稳定性。 六、代码质量管理与重构 SpringSide 4 遵循良好的编码规范,如SOLID原则,以及代码重构的最佳实践。通过学习...
《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练...
《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练...
8. **单元测试与TDD**:熟悉Junit进行单元测试,了解Dbunit和EasyMock,实践测试驱动开发(TDD),确保代码质量。 9. **Web前端技术**:熟练掌握HTML、CSS和JavaScript,运用jQuery、Ajax、Vue.js、Bootstrap和...
#### 十、使用JUnit和DBUnit进行单元测试(Unit testing with JUnit and DBUnit) 第十章专注于如何为Hibernate应用程序编写单元测试。这部分内容对于确保代码质量和可维护性至关重要。它首先介绍了如何使用JUnit...
DbUnit是Java中用于数据库测试的工具,而"express"可能表示它是轻量级且易于使用的。这个工具可能简化了数据库的初始化、数据导入导出,以及测试数据的准备,使得数据库相关的测试工作更为高效。 总的来说,Holy J...
- `dbunit-license.txt`:DbUnit库的许可证文件,可能用于数据库相关的测试和数据初始化。 **总结** BeRSeRK作为一个开源的Java执行监视器,利用拦截滤波器设计模式提供强大的监控功能,包括访问控制、日志记录...