技术开发 频道

测试分类技术与Testng

【IT168技术文档】摘要
  TestNG是一种基于注释的测试框架,通过添加诸如灵活的装置、测试分类、参数测试和依赖方法等特性来克服JUnit的一些不足之处。此外,TestNG运行于Java 5.0(通过注释)和Java 1.4(通过JavaDoc样式的注释)之上。由于TestNG可以轻松地将开发人员测试分类成单元、组件和系统组,因此能够使构建时间保持在可管理的范围内。通过使用group注释和多重Ant任务,测试组可以不同的频率运行于一台工作站之上或持续集成环境中。

  本文分析了测试分类的概念,演示了如何将TestNG的group注释与灵活的测试装置具相结合,通过特定的Ant目标促进以不同频率运行的测试。本文假设您了解TestNG。

TestNG组的研究
  TestNG支持一种直观的机制,用于分组测试类和相关测试方法。在最基本的层面上,TestNG的分组特性是通过test注释的groups参数启用的,它可附加到类或者单个方法。从其名称即可看出,一个类或单个方法可属于1至n组。

  例如,下面的类包含两个公共方法,缺省标为测试并进一步分类为属于one组:
/** * @testng.test groups="one" */ public class SimpleGroupedTest { private String fixture; /** * @testng.before-class = "true" */ private void init(){ this.fixture = "Hello World"; } public void verifyEquality(){ Assert.assertEquals("Hello World", this.fixture); } public void verifySame(){ String value = this.fixture; Assert.assertSame(this.fixture, value); } }
相反,下一个类定义了两个测试方法。然而,一个方法却属于两个不同的组——one和two。相应地,任何相关装置逻辑都必须与其所需的一个组关联。本例中,在组one或组two执行之前,必须首先将init()方法配置为运行。
public class SimpleGroupedTwoTest { private String fixture; /** * @testng.before-class = "true" \ * groups = "one, two" */ private void init(){ this.fixture = "Hello World"; } /** * @testng.test groups="one, two" */ public void verifyEqualityAgain(){ Assert.assertEquals(this.fixture, "Hello World"); } /** * @testng.test groups="two" */ public void verifySameAgain(){ String value = this.fixture; Assert.assertSame(value, this.fixture); } }
TestNG支持以多种方式运行所需组,从通过TestNG Eclipse插件指定这些组一直到在TestNG Ant任务中列举它们。

测试分类简介

  要验证正在工作的软件,最简单的方法之一就是执行一次构建(即编译源代码并执行测试);因此,长时间运行的构建是降低开发人员生产力的一项因素,这一点也不令人吃惊。不得不等待那超长的构建过程完成,几乎没有什么比这更恼人的了。可与之相提并论的就是编码过程中遇到意外的蓝屏和重启,但我们至少能够很容易地对时间较长的构建做点什么。

  长时间构建的原因几乎总是测试执行这个步骤(除非是有数百万的.java 文件)。此外,如果存在大量的设置步骤,例如配置数据库或是部署一个 EAR 文件,执行一个测试套件的总时间倾向于变长。所以,精心设计一个测试分类策略并按照规定的时间间隔运行分类有利于获得可管理的构建持续时间。

  然而,分类测试要求我们定义具体的分类,即细化单元测试。单元测试就像一张三层饼的一片,另两片则是组件测试和系统测试。下一节分析了开发人员通常会编写的不同类型的测试,诸如单元测试、组件测试和系统测试。随后,它们将在TestNG中执行,并集成到一个Ant 构建脚本里。

单元测试定义

  单元测试验证独立对象的行为;然而,由于类的耦合,单元测试也能验证相关对象的行为。例如,下面的单元测试验证了对象身份,它是在TestNG中实现的,只关注一个类型:PartOfSpeechEnum。

/** * @testng.test */ public class PartOfSpeechEnumTest { public void verifyNotEquals() throws Exception{ assert PartOfSpeechEnum.ADJECTIVE != PartOfSpeechEnum.NOUN: "NOUN == ADJECTIVE?"; } public void verifyEquals() throws Exception{ assert PartOfSpeechEnum.ADJECTIVE == PartOfSpeechEnum.ADJECTIVE "ADJECTIVE != ADJECTIVE"; } }
有些时候,一个单元测试会验证多个对象的行为。然而,这些对象很少有其他的外部依赖项。例如,下面的测试验证了两个对象:Dependency和DependencyFinder。
//imports removed... public class DependencyFinderTest { private DependencyFinder finder; /** * @testng.test */ public void verifyFindDependencies() throws Exception{ String targetClss = "test.com.sedona.frmwrk.dep.Finder"; Filter[] filtr = new Filter[] { new RegexPackageFilter("java|org")}; Dependency[] deps = finder.findDependencies(targetClss, filtr); Assert.assertNotNull(deps, "deps was null"); Assert.assertEquals(deps.length, 5, "should be 5 large"); } /** * @testng.before-class = "true" */ protected void init() throws Exception { this.finder = new DependencyFinder(); } }
 要牢记的一个要点就是:单元测试不依靠外部依赖项,如数据库。数据库会增加设置和运行测试的时间。单元测试没有配置成本(就时间来度量),运行它的资源消耗可忽略不计。

  单元测试运行很快,所以只要运行了一次构建,就应该运行单元测试,包括在持续集成环境中也是如此。在持续集成环境中,如果源储存库(如CVS)发生变更,通常就要运行构建。

0