当前位置:天才代写 > tutorial > JAVA 教程 > 追求代码质量 – 可反复的系统测试

追求代码质量 – 可反复的系统测试

2017-11-10 08:00 星期五 所属: JAVA 教程 浏览:365

副标题#e#

在本质上,像 JUnit 和 TestNG 一样的测试框架利便了可反复性测试的建设 。由于这些框架操作了简朴 Boolean 逻辑(以 assert 要领的形式)的靠得住性 ,这使得无工钱过问而运行测试成为大概。事实上,自动化是测试框架的主要优 点之一 —— 我可以或许编写一个用于断言详细行为的相当巨大的测试,且一旦这些 行为有所改变,框架就会陈诉一小我私家人都能大白的错误。

操作成熟的测试框架会带来框架 可反复性的利益,这是显而易见的。但逻辑 的 可反复性却取决于您。譬喻,思量建设用于验证 Web 应用措施的可反复测试 的环境,一些 JUnit 扩展框架(如 JWebUnit 和 HttpUnit)在协助自动化的 Web 测试方面很是好用。可是,使测试的 plumbing 可反复则是开拓人员的任务 ,而这在陈设 Web 应用措施资源时很难举办。

实际的 JWebUnit 测试的结构进程相当简朴,如清单 1 所示:

清单 1. 一个简朴的 JWebUnit 测试

package test.come.acme.widget.Web;

import net.sourceforge.jwebunit.WebTester;
import junit.framework.TestCase;

public class WidgetCreationTest extends TestCase {
  private WebTester tester;

  protected void setUp() throws Exception {
  this.tester = new WebTester();
  this.tester.getTestContext().
   setBaseUrl("http://localhost:8080/widget/");
  }

  public void testWidgetCreation() {
  this.tester.beginAt("/CreateWidget.html");
  this.tester.setFormElement("widget-id", "893-44");
  this.tester.setFormElement("part-num", "rt45-3");

  this.tester.submit();
  this.tester.assertTextPresent("893-44");
  this.tester.assertTextPresent("successfully created.");
  }
}

这个测试与一个 Web 应用措施通信,并试图建设一个基于该交互的小部件。 该测试随后校验此部件是否被乐成建设。读过本系列之前部门的读者们也许会注 意到该测试的一个微妙的可反复性问题。您留意到了吗?假如这个测试用例持续 运行两次会奈何呢?

由这个小部件实例(即,widget-id)的验证方面可以判定出,可以安详地做 出这样的假设,即此应用措施中的数据库约束很大概会阻止建设一个已经存在的 特另外小部件。由于缺少了一个在运行另一个测试前删除此测试用例的方针小部 件的进程,假如再持续运行两次,这个测试用例很是有大概会失败。

幸运的是,如前面文章中所探讨的那样,有一个有助于数据库-依赖性 (database-dependent)测试用例可反复性的机制 —— 即 DbUnit。


#p#副标题#e#

利用 DbUnit

改造 清单 1 中的测试用例来利用 DbUnit 长短常简朴的。DbUnit 只需要一 些插入数据库的数据和一个相应的数据库毗连,如清单 2 所示:

清单 2. 用 DbUnit 举办的数据库-依赖性测试

package test.come.acme.widget.Web;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.operation.DatabaseOperation;

import net.sourceforge.jwebunit.WebTester;
import junit.framework.TestCase;

public class RepeatableWidgetCreationTest extends TestCase  {
  private WebTester tester;

  protected void setUp() throws Exception {
  this.handleSetUpOperation();
  this.tester = new WebTester();
  this.tester.getTestContext().
   setBaseUrl("http://localhost:8080/widget/");
  }

  public void testWidgetCreation() {
  this.tester.beginAt("/CreateWord.html");
  this.tester.setFormElement("widget-id", "893-44");
  this.tester.setFormElement("part-num", "rt45-3");

  this.tester.submit();
  this.tester.assertTextPresent("893-44");
  this.tester.assertTextPresent("successfully created.");
  }

  private void handleSetUpOperation() throws Exception{
  final IDatabaseConnection conn = this.getConnection ();
  final IDataSet data = this.getDataSet();
  try{
   DatabaseOperation.CLEAN_INSERT.execute(conn, data);
  }finally{
   conn.close();
  }
  }

  private IDataSet getDataSet() throws IOException,  DataSetException {
  return new FlatXmlDataSet(new File ("test/conf/seed.xml"));
  }

  private IDatabaseConnection getConnection() throws
   ClassNotFoundException, SQLException {
   Class.forName("org.hsqldb.jdbcDriver");
   final Connection jdbcConnection = 
    DriverManager.getConnection ("jdbc:hsqldb:hsql://127.0.0.1",
   "sa", "");
   return new DatabaseConnection(jdbcConnection);
  }
}

#p#分页标题#e#

插手了 DbUnit,测试用例真的是可反复的了。在 handleSetUpOperation() 要领中,每当运行一个测试用例时,DbUnit 对数据执行一个 CLEAN_INSERT。此 操纵本质大将一个数据库的数据清空并插入一个新的数据集,从而删除任何之前 建设的小部件。

#p#副标题#e#

再一次探讨什么是 DbUnit?

DbUnit 是一个 JUnit 扩展,用于在运行测试时将数据库放入一个已知状态中。开拓人员利用 XML 种子文件将特定命据插入到测试用例所依赖的数据库中。因而,DbUnit 便 利了依赖于一个或多个数据库的测试用例的可反复性。

但那并不料味着 已经竣事了对测试用例可反复性这一话题的探讨。事实上,一切才方才开始。

反复系统测试

我喜欢将 清单 1 和 清单 2 中界说的测试用例称 为系统测试。因为系统测试运行安装完整的应用措施,如 Web 应用措施,它们 凡是包括一个 servlet 容器和一个相关联的数据库。这些测试的目标在于校验 那些设计为端对端操纵的外部接口(如 Web 应用措施中的 Web 页面)。

弹性优先级
作为总体法则,应在任何大概的时候制止测试用例继 承。很多 JUnit 扩展框架都提供特定的可担任测试用例,以便利于测试一个特 定的架构。然而由于 Java™ 平台的单一担任典型,使得从框架中担任类 的测试用例饱受缺乏弹性之苦。凡是,这些沟通的 JUnit 扩展框架提供了署理 API,这使得连系各类不具有严格担任布局的框架变得十分简朴。

由于设 计它们的目标是为了测试成果完整的应用措施,因而系统测试趋向于增加运行次 数而不是淘汰配置测试的总时间。譬喻,清单 1 和 清单 2 中展示的逻辑测试 在运行前 需要下列步调:

建设一个 war 文件,该文件包括所有相关 Web 内容,如 JSP 文件、servlet、第三方的 jar 文件、图像等。

将此 war 文件陈设到方针 Web 容器中。(假如该容器尚未启动,启动该容 器。)

启动任何相关的数据库。(假如需要更新数据库模式,在启动前举办更新。 )

此刻,对付一个微不敷道的小测试要做大量的帮助性事情!假如证明这个过 程是耗时的,那么您认为这个测试会隔断多长时间运行一次呢?面临要使系统测 试在逻辑上可反复(在一个持续的集成情况中)这一需求,这个步调列表简直令 人望而生畏。

先容 Cargo

好动静是可以在之前的列表中使所有主要配置步调自动化。事实上,假如恰 好从事过 Java Web 开拓,大概已经用 Ant、Maven 或其他构建东西使步调 1 自动化了。

步调 2 却是一个有趣的障碍。自动化一个 Web 容器照旧需要必然能力的。 譬喻,一些容器具有定制的 Ant 任务,这些任务利便了其自动陈设及运行,但 这些任务是特定于容器的。并且,这些任务尚有一些假设,如容器的安装位置, 尚有更重要的是,容器已被安装。

Cargo 是一个致力于以通用方法自动化容器打点的创新型开源项目,因而用 于将 WAR 文件陈设到 JBoss 的沟通的 API 也可以或许启动及遏制 Jetty。Cargo 也能自动下载并安装一个容器。可以以差异的方法操作 Cargo 的 API,从 Java 代码到 Ant 任务,再到 Maven 方针。

运用一个如 Cargo 这样的东西,应对了在编写合乎逻辑可反复的测试用例中 碰着的主要问题之一。别的,还可以结构一个构建用于驾御 Cargo 的成果以 自 动地完成下列任务:

#p#副标题#e#

下载一个所期望的容器。

安装该容器。

启动该容器。

将一个选定的 WAR 或 EAR 文件陈设到该容器中。

很简朴,是吧?接下来,您还可以或许用 Cargo 遏制一个选定的容器。

“谈谈” Cargo

在深入 Cargo 前,最好先相识一下 Cargo 的基本常识。也就是说,由于 Cargo 与容器及容器打点相关,所以要领略了容器及容器打点的有关观念。

#p#分页标题#e#

对付新手,显然要先相识容器 的观念。容器是用以寄存应用措施的处事器。 应用措施可以是基于 Web 的,基于 EJB 的,或基于这两者的,这就是为什么有 Web 容器和 EJB 容器的原因。Tomcat 是 Web 容器,而 JBoss 则会被认为是 EJB 容器。因此,Cargo 支持相当多的容器,但在我的例子中,我将利用 Tomcat 5.0.28 版。(Cargo 将称其为“tomcat5x”容器。)

接下来,假如尚未安装容器,可以利用 Cargo 来下载并安装一个特定的容器 。为此,需要提供应 Cargo 一个下载 URL。一旦安装了容器,Cargo 也会答允 利用设置选项 来对其举办设置。这些选项以名称-值对的形式存在。

最后,要先容可陈设资源 的观念,在我的例子中即 WAR 文件。请留意 EAR 文件也是一样的简朴。

将这些观念记着,让我们来看一下可以用 Cargo 来完成什么任务。

Cargo 实践

本文中的例子涉及到在 Ant 中利用 Cargo,这就必须将之前界说的系统测试 和 Cargo Ant 任务包装在一起。这些任务随后安装、启动、陈设并遏制容器。 我们将首先举办安装配置,运行测试然后遏制容器。

在 Ant 构建中利用 Cargo 所需的第一步是提供一个针对所有的 Cargo 任务 的任务界说。这一步答允随后在构建文件中引用 Cargo 任务。应付这一步有很 多的要领。清单 3 简朴地装载了来自 Cargo JAR 文件中的属性文件的任务:

清单 3. 在 Ant 中装载所有的 Cargo 任务

<taskdef  resource="cargo.tasks">
  <classpath>
  <pathelement location="${libdir}/${cargo-jar}"/>
  <pathelement location="${libdir}/${cargo-ant-jar}"/>
  </classpath>
</taskdef>

一但界说了 Cargo 的任务,真正的动作就开始了。清单 4 界说了下载、安 装及启动 Tomcat 容器的 Cargo 任务。zipurlinstaller 任务将 Tomcat 从 http://www.apache.org/dist/tomcat/tomcat-5/v5.0.28/bin/ jakarta- tomcat-5.0.28.zip 中下载并安装到一个当地姑且目次中。

清单 4. 下载并启动 Tomcat 5.0.28

<cargo  containerId="tomcat5x" action="start"
     wait="false" id="${tomcat-refid}">

  <zipurlinstaller installurl="${tomcat-installer-url}"/>

  <configuration type="standalone" home="${tomcatdir}">
  <property name="cargo.remote.username" value="admin"/>
  <property name="cargo.remote.password" value=""/>

  <deployable type="war" file="${wardir}/${warfile}"/>

  </configuration>

</cargo>

请留意要想如您所愿,从差异的任务中启动和遏制一个容器,必须将容器同 一个惟一的 id 接洽起来,此 id 是 cargo 任务的 id="${tomcat-refid}"。

还要留意的是,Tomcat 的设置是在 cargo 任务内处理惩罚的。在 Tomcat 中, 必须配置 username 和 password 属性。最后,利用 deployable 元素界说一个 指向 WAR 文件的指针。

#p#副标题#e#

Cargo 属性

Cargo 任务顶用到的所有属性都显示在清单 5 中。譬喻,tomcatdir 界说 Tomcat 将安装的两个位置中的一个。这个出格的位置是一个镜像布局,该位置 将被实际下载并安装的 Tomcat 实例(在姑且目次中找到的)所引用。tomcat- refid 属性则辅佐将容器中惟一的实例与其镜像关联起来。

清单 5. Cargo 属性

<property name="tomcat-installer- url"
  value="http://www.apache.org/dist/tomcat/tomcat-5/v5.0.28/bin/
   jakarta-tomcat-5.0.28.zip"/>
<property name="tomcatdir" value="target/tomcat"/>
<property name="tomcat.username" value="admin"/>
<property name="tomcat.passwrd" value=""/>
<property name="wardir" value="target/war"/>
<property name="warfile" value="words.war"/>
<property name="tomcat-refid" value="tmptmct01"/>

为遏制一个容器,可以界说一个引用 tomcat-refid 属性的任务,如清单 6 所示。

清单 6. 按 Cargo 方法遏制容器

<cargo  containerId="tomcat5x" action="stop"
     refid="${tomcat-refid}"/>

用 Cargo 封装

清单 7 将 清单 4 和清单 6 中的代码连系起来,用两个 Cargo 任务封装了 一个测试方针:一个用于启动 Tomcat,另一个用于遏制 Tomcat。antcall 任务 挪用在清单 8 中界说的名为 _run-system-tests 的方针。

清单 7. 用 Cargo 封装测试方针

#p#分页标题#e#

<target name="system- test" if="Junit.present"
     depends="init,junit-present,compile-tests,war">

  <cargo containerId="tomcat5x" action="start"
     wait="false" id="${tomcat-refid}">
  <zipurlinstaller installurl="${tomcat-installer-url}"/>
  <configuration type="standalone" home="${tomcatdir}">
   <property name="cargo.remote.username" value="admin"/>
   <property name="cargo.remote.password" value=""/>
   <deployable type="war" file="${wardir}/${warfile}"/>
  </configuration>
  </cargo>

  <antcall target="_run-system-tests"/>

  <cargo containerId="tomcat5x" action="stop"
     refid="${tomcat-refid}"/>

</target>

#p#副标题#e#

清单 8 界说测试方针,称作 _run-system-tests。请留意此任务只 运行置 于 test/system 目次下的系统测试。譬喻,清单 2 中界说的测试用例就位于这 个目次下。

清单 8. 通过 Ant 运行 JUnit

<target name="_run-system- tests">
  <mkdir dir="${testreportdir}"/>
  <junit dir="./" failureproperty="test.failure"
     printSummary="yes" fork="true"
  haltonerror="true">
  <sysproperty key="basedir" value="."/>
  <formatter type="xml"/>
  <formatter usefile="false" type="plain"/>
  <classpath>
   <path refid="build.classpath"/>
   <pathelement path="${testclassesdir}"/>
   <pathelement path="${classesdir}"/>
  </classpath>
  <batchtest todir="${testreportdir}">
   <fileset dir="test/system">
   <include name="**/**Test.java"/>
   </fileset>
  </batchtest>
  </junit>
</target>

在 清单 7 中,完整地设置了 Ant 构建文件,从而将系统测试与 Cargo 部 署封装在一起。清单 7 中的代码确保了清单 8 中 test/system 目次下的所有 系统测试都是逻辑上可反复的。可以在任何时间里在任何呆板上运行这些系统测 试,对付持续集成情况尤佳。该测试对容器未做任何假设 —— 未对位置做假设 ,甚至未对其是否运行做假设!(虽然,这些测试仍做了一个假设,我没有强调 ,即潜在的数据库是设置精采且在运行中的。但那又是另一个要接头的主题了。 )

可反复的功效

在清单 9 中,可以看到事情的成就。当将 system-test 呼吁宣布到 Ant 构 建后,就会执行系统测试。Cargo 处理惩罚打点所选容器的所有细节,不需要对测试 情况作出绝对反复性假设。

清单 9. 加强的构建

war:
  [war] Building war:  C:\dev\projects\acme\target\widget.war

system-test:

_run-system-tests:
  [mkdir] Created dir: C:\dev\projects\acme\target\test- reports
  [junit] Running  test.come.acme.widget.Web.RepeatableWordCreationTest
  [junit] Tests run: 1, Failures: 0, Errors: 0, Time  elapsed: 4.53 sec
  [junit] Testcase: testWordCreation took 4.436 sec

BUILD SUCCESSFUL
Total time: 1 minute 2 seconds

请记着,Cargo 也在 Maven 构建中起浸染。别的,从正常的应用措施到测试 用例,Cargo Java API 都有助于容器的措施化打点。且 Cargo 不只合用于 JUnit(尽量样例代码是用 JUnit 写的),TestNG 用户将会很兴奋地相识到 Cargo 对其测试套件也起浸染。事实上,测试用什么编写并不重要,重要的是将 它们同 Cargo 封装起来,容器打点问题就会迎刃而解!

竣事语

您的测试是否在逻辑上可反复由您来抉择,可是通过本文您确实看到 Cargo 简直很有用处。Cargo 打点容器情况,所以您就可以不消打点。将 Cargo 包括 到您的测试例程中 —— 这毫无疑问会减轻您结构用于验证 Web 应用措施的可 反复测试的承担。

 

    关键字:

天才代写-代写联系方式