Spring+iBatis+DBUnit 进行单元测试

发表于:2018-11-23 21:50

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:施媛波    来源:网络

  在很多成功的软件项目中,测试自动化往往是关键的层面。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);
  }
  }
  }

  上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
 
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号