首页 > 编程语言 >TestNG 使 Java 单元测试轻而易举

TestNG 使 Java 单元测试轻而易举

时间:2023-07-28 15:32:16浏览次数:50  
标签:Java 单元测试 testng 测试 TestNG 方法 JUnit


http://www.ibm.com/developerworks/cn/java/j-testng/

在每个现代软件包的构造阶段,测试这一实践都扮演着中心角色。过去那种先编写代码,然后有空的时候再测试(或者根本不测试)的日子已经一去不返,因为大多数开发人员现在认识到需要采用编码和测试彼此交织、同步推进的软件方法论,以便尽早发现 bug,在开发过程开始的时候就识别出主要的风险。

JUnit 超过了其他测试框架,推动开发人员理解了测试尤其是单元测试的用途。利用一个相当简单、实用、严格的架构,JUnit 已经能够“传染”大量开发人员。(有关“被测试传染”的更多信息,请参阅 参考资料。) JUnit 用户已经学会了单元测试的一些基本规则:

  • 每段代码都必须经过测试。
  • 只要有可能,代码的测试必须隔离进行(例如,使用像 模拟对象这样的技术 )。
  • 软件必须容易测试 —— 也就是说, 在编写的时候要想着测试

但是,随着开发人员对测试的信任增长,JUnit 的简单性和严格性把他们分成两个相反的派别。一方面,有些人坚信 JUnit 的简单性对于不断地提醒程序员软件也必须保持简单来说是必不可少的(这称为 KISS 原则,代表 keep it simple, stupid);另一方面,有些人认为 JUnit 不是简单而是简化,所以他们想要从测试框架得到新的高级特性、更大的灵活性和更强大的能力。JUnit 的一些特殊特性,就是为了满足这个群体的一些具体批评而推出的:

  • 因为 Java 语言的单继承性,所以必须扩展 TestCase类的限制很大。
  • 无法向 JUnit 的测试方法传递参数,也无法向 setUp()tearDown()方法传递参数。
  • 执行模型有点奇怪:每次执行一个测试方法的时候,都要重新实例化测试类。
  • 管理复杂项目中的不同测试套件有可能非常复杂。

TestNG 的创造者



TestNG 的创造者是 Cedric Beust,他在 Java 编程领域非常出名,是 EJB 3 专家组的成员,也是其他一些流行的开源项目(例如 EJBGen 和 Doclipse)的创造者。TestNG 在 Apache 软件许可条款约束下发布,并可从其 Web 站点下载(请参阅 参考资料中到该站点和 Cedric 站点的链接)。


在本文中,您将学习到如何用这个叫做 TestNG的新测试框架为应用程序编写单元测试。TestNG 的灵感来自 JUnit,同时尽量保持后者的简单性;但是,TestNG 消除了老框架的大多数限制,使开发人员可以编写更加灵活、更加强大的测试。由于 TestNG 大量借用 Java Annotation(随 JDK 5.0 引入;有关这个新特性的更多信息,请参阅 参考资料)来定义测试,所以本文也可以向您演示如何在实际的生产环境中使用 Java 语言的这个新特性。

关于代码

为了演示 TestNG 的用法,我要为叫做 Jakarta Common Lang 的这个广泛应用的开源库(其中包含一些处理和操纵字符串、数字和 Java 对象的有用的类)编写一些单元测试。在下面的 参考资料一节中,您可以找到 TestNG 和 Jakarta Common Lang 库的链接;如果您想在自己的机器上随着本文一起练习,这二者都需要下载。

可以在两个不同的包中得到 TestNG:一个包要求 JDK 5.0,另一个包与 Java 语言 1.4 版本兼容。定义测试的时候,它们使用的语法略有差异:前者使用 JDK 5.0 标注,后者使用旧的 Javadoc 风格的标注。本文使用的是 JDK 5.0 版本,所以在继续阅读本文之前,需要对标注有基本的了解;您可以在 参考资料中找到关于这个主题的 developerWorks 资源的链接。但是,您要知道 只有在编译和运行测试的时候才需要 JDK 5.0,所以您仍然可以用自己喜欢的编译器来构建应用程序。实际上,您将用从 Jakarata 项目的 Web 站点下载的相同 JAR 文件来测试 Jakarta Common Lang 库。关于使用 Java 平台 1.4 版本的 TestNG 的更多细节,可以在 TestNG 的 Web 站点上找到。

最后,请单击本文顶部或底部的 Code图标,下载 j-testng-sample.zip 文件,其中包含一些示例,演示了如何用 TestNG 为 Jakarta Commons Lang 编写单元测试。在里面,可以找到这里给出的大多数代码,还有其他一些示例。阅读本文并不需要这些代码,但是它可以帮助您更加深入地理解在这里介绍的概念。




回页首

TestNG 快速起步

TestNG 的测试类是普通的老式 Java 对象;您不需要扩展任何特殊的类,也不需要使用测试方法的任何命名约定:您只要用标注 @Test通知框架这个类的方法是测试。清单 1 演示了实用类 StringUtils的一个最简单的测试。它测试 StringUtils的两个方法: isEmpty()方法检测 String是否为空; trim()方法从 String两端删除控制字符。请注意,其中使用了 Java 指令 assert来检测错误情况。

清单 1. 针对类 StringUtils 的一个测试用例

package tests; 
 import com.beust.testng.annotations.*; 
 import org.apache.commons.lang.StringUtils; 
 public class StringUtilsTest 
 { 
	 @Test 
	 public void isEmpty() 
	 { 
		 assert StringUtils.isBlank(null); 
		 assert StringUtils.isBlank(""); 
	 } 
	 @Test 
	 public void trim() 
	 { 
		 assert "foo".equals(StringUtils.trim("  foo   ")); 
	 } 
 } 


但是,在运行测试之前,必须用特殊的 XML 文件配置 TestNG,习惯上把这个文件命名为 testng.xml。这个文件的语法非常简单,如清单 2 所示。这个文件首先定义测试套件 My test suite,这个套件只包含一个测试 First test,这个测试由 StringUtilsTest类完成。

清单 2. TestNG 的配置文件

				
 <!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" > 
 <suite name="My test suite"> 
  <test name="First test"> 
    <classes> 
       <class name="tests.StringUtilsTest" /> 
    </classes> 
  </test> 
 </suite> 


如果这个示例 testng.xml 文件看起来没什么用处(只有一个测试类),那么好消息是:这实际上是您定义测试套件时 惟一需要编写的文件。还记得 JUnit 过去的日子么?在那些日子里,套件的定义可能分布在多个文件中:JUnit 的 TestSuite文件,属性文件,还有当然缺不了的 Ant 构建文件。使用 TestNG,所有必需的数据都集中在 testng.xml 文件中。不需要额外的 TestSuite文件和构建文件。

要运行测试,请用 javac编译类,然后用以下命令调用 TestNG :

java -ea -classpath .;testng.jar;commons-lang-2.0.jar com.beust.testng.TestNG testng.xml


在这里,选项 -ea告诉 JVM 处理断言(在断言失败时抛出异常);运行这个例子只需要 testng.jar 和 commons-lang-2.0.jar 这两个库,而 com.beust.testng.TestNG是 TestNG 的主类。对于所有那些已经非常高兴地忘记了 javajavac的神秘语法的开发人员来说,还提供了一个有用的 Ant 任务。作为例子,清单 3 演示了本文发布的示例应用程序的 Ant 构建文件。请注意与类 com.beust.testng.TestNGAntTask关联的 testng任务的定义,以及它在 test目标中相当简单的用法。

清单 3. 带有 TestNG 任务的 Ant 构建文件

				
 <project name="sample" default="test" basedir="."> 
   <!-- COMPILE TESTS--> 
   <path id="cpath"> 
      <pathelement location="testng.jar"/> 
      <pathelement location="commons-lang-2.0.jar"/> 
   </path> 
   <target name="compile"> 
      <echo message="compiling tests"/> 
      <mkdir dir="classes"/> 
      <javac   debug="true"
         source="1.5" classpathref="cpath"
         srcdir="src" destdir="classes"/> 
   </target> 
   <!-- RUN TESTS--> 
   <taskdef name="testng"
      classname="com.beust.testng.TestNGAntTask"
      classpathref="cpath"/> 
   <path id="runpath"> 
      <path refid="cpath"/> 
      <pathelement location="classes"/> 
   </path> 
   <target name="test" depends="compile"> 
      <echo message="running tests"/> 
      <testng fork="yes" classpathref="runpath" outputDir="test-output"> 
         <fileset dir="src" includes="testng.xml"/> 
         <jvmarg value="-ea" /> 
      </testng> 
   </target> 
 </project> 
     


如果一切正常,那么应当在控制台中看到测试结果。而且,TestNG 还在当前目录下自动创建了一个叫做 test-output 的文件夹,并在其中创建了一份非常好的 HTML 报告。如果打开该报告并装入 index.html,就可以看到与图 1 中的页面类似的页面。

图 1. TestNG 创建的 HTML 报告




回页首

定义测试组

TestNG 另外一个有趣的特性是其定义测试组的能力。每个测试方法都可以与一个或多个组相关联,但可以选择只运行某个测试组。要把测试加入测试组,只要把组指定为 @Test标注的参数,使用的语法如下:

@Test(groups = {"tests.string"})


在这个具体的例子中,您声明:标注的方法属于 tests.string组。因为参数 groups是一个数组,所以可以指定多个组,组名之间用逗号分隔。例如,在示例应用程序中,您可以为 String、Number 以及 boolean 创建不同的测试,然后如清单 4 所示配置 TestNG, 有选择地运行它们 .

清单 4. 带有不同组的配置文件

				
 <!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" > 
 <suite name="My suite"> 
  <test name="Simple example"> 
    <groups> 
      <run> 
		 <include name="tests.string" /> 
		 <include name="tests.math" /> 
		 <exclude name="tests.boolean"/> 
      </run> 
    </groups> 
    <classes> 
		 .... list classes here.... 
    </classes> 
  </test> 
 </suite> 


显然,当运行不同的测试组时,HTML 报告能够在单一列表中显示所有测试,也可以在独立的列表中显示每个组的测试,从而能够立即理解问题的来源。




回页首

配置方法

使用 TestNG,不仅可以指定测试方法,还可以用专门的标注 @Configuration指定类中的其他特定方法,这些方法叫做 配置方法。配置方法有四种类型:

  • beforeTestClass方法在类实例化之后,但是在测试方法运行之前执行。
  • afterTestClass方法在类中的所有测试方法执行之后执行。
  • beforeTestMethod方法在类中的任何测试方法执行之前执行。
  • afterTestMethod方法在类中的每个测试方法执行之后执行。

图 2 进一步描述了测试类的生命周期。

图 2. 测试类的生命周期

清单 5 演示了配置方法的一些示例。请注意,如果您使用组,那么配置方法也必须属于某个组。而且,配置方法的四种类型彼此之间不是互斥的,所以可以把方法定义成同时属于一种或多种配置方法类型。(作为例子,请参阅清单 5 中的 aroundTestMethods()方法)。

清单 5. 配置方法示例

 @Configuration(beforeTestClass = true, groups = {"tests.workflow"}) 
 public void setUp() 
 { 
   System.out.println("Initializing..."); 
 } 
 @Configuration(afterTestMethod = true, beforeTestMethod = true
    , groups = {"tests.workflow"}) 
 public void aroundTestMethods() 
 { 
   System.out.println("Around Test"); 
 } 


TestNG 中的配置方法是 JUnit 的 setUp()tearDown()方法的增强版;它们的主要目的是为测试创建正确的执行上下文,并在测试用例执行之后刷新数据。




回页首

异常检测

使用 TestNG,您可以非常简单、非常容易地检测异常的发生。很明显,用 JUnit 也可以做这件事,但是正如您在清单 6 中的示例中所看到的,使用 TestNG 的 @ExpectedExceptions标注可以使代码编写惊人地容易和简单。 @ExpectedExceptions标注指明框架能够容忍抛出的 NumberFormatException异常,所以不应当被当作是故障。要查看在某行代码中是否抛出异常,您可以直接在这行代码之后加入 assert false语句。这意味着 只有在指定行中抛出特定类型的异常的时候,您才会通过测试。

清单 6. 用 TestNG 进行异常检测

 public class  NumberUtilsTest 
 { 
	 @Test(groups = {"tests.math"}) 
	 @ExpectedExceptions(NumberFormatException.class) 
	 public void test() 
	 { 
		 NumberUtils.createDouble("12.23.45"); 
		 assert false;  //shouldn't be invoked 
	 } 
 } 





回页首

结束语

在本文中,我提供了 TestNG 的快速实用介绍,目的是展示如何开始编写单元测试。但是,它不是一份完整的参考手册。TestNG 还有许多其他非常有用的有趣特性:

  • 可以向测试方法和配置方法传递参数,可以用标注或在 XML 配置文件中声明参数。
  • 可以在 TestNG 下用“兼容模式”运行过去运行良好的 Junit 测试。
  • 可以在测试组之间建立依赖性,决定它们的执行顺序。

要了解这个框架的所有潜力,有必要参阅 TestNG 的文档(参阅 参考资料)。

所有这些特性,与用于定义测试的 Java 标注一起,使整个测试过程更加简单、更加灵活。编写测试必须遵守的规则 很少;除此之外,您绝对可以自由选择自己喜欢的测试策略。

在使用 TestNG 时最明显的是,这个模板已经是编写单元测试的一个好选择了,而且,在设计上,它与其他库和工具的集成非常简单,所以它未来的发展会给开发人员带来一些有趣的新东西。





回页首

下载

名字

大小

下载方法

j-testng-sample.zip

 

HTTP

关于下载方法的信息

 

参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文
  • 请单本文顶部或底部的 Code图标下载 j-testng-sample.zip,其中包含本文中讨论的代码。
  • 在项目的 Web 站点下载 TestNG。
  • 运行本文的代码,需要下载 Jakarta Commons Lang库。
  • 如果不熟悉 Java 标注,请阅读 Brett McLaughlin 2004 年 9 月份开始在 developerWorks 上发表的由两部分组成的系列文章:

关于作者




TestNG 使 Java 单元测试轻而易举_测试

Filippo Diotalevi 是位于米兰的 IBM 意大利公司的 IT 专家,他主要是 J2EE 应用程序开发人员。他的兴趣领域主要是模式、面向方面编程,以及敏捷方法论;他是 IBM 红皮书 Pattern: Direct Connections for Intra- and Inter-enterprise 的合著者,以及发表在 Web 站点和杂志上的一些技术文章的作者,还是 Java User Group Milano 的创始人。


 

标签:Java,单元测试,testng,测试,TestNG,方法,JUnit
From: https://blog.51cto.com/u_16174476/6883818

相关文章

  • java多线程学习-java.util.concurrent详解
    java多线程学习-java.util.concurrent详解(一)Latch/Barrier   Java1.5提供了一个非常高效实用的多线程包:java.util.concurrent,提供了大量高级工具,可以帮助开发者编写高效、易维护、结构清晰的Java多线程程序。从这篇blog起,我将跟大家一起共同学习这些新的Java多线......
  • Java a=a+b和a+=b的区别
    1、对于同样类型的a,b来说两个式子执行的结果确实没有什么区别。但是从编译的角度看吧(武让说的),a+=b;执行的时候效率高。2、对于不同类型的a,b来说2.1不同类型的两个变量在进行运算的时候,我们经常说到的是类型的转换问题。这里,记住两点:一、运算过程中,低精度的类型向高精度类型转......
  • Java面试题 P11:ArrayList和LinkedList区别
    ArrayList:基于动态数组,连续内存存储,适合下标访问(随机访问),扩容机制:因为数组长度固定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会涉及到元素的移动(往后复制一份,插入新元素),使用尾插法并指定初始容量可以极大提升性能,甚至超过linkedListLin......
  • Java定时弹窗提示与展示图片
    自顶向下介绍,首先是定时弹窗功能,可以用Java自带的ScheduledExecutorService库完成函数调用。packagehealthReminder;importjava.util.concurrent.Executors;importjava.util.concurrent.ScheduledExecutorService;importjava.util.concurrent.TimeUnit;publicclassT......
  • centos俩个java版本共存
    环境背景centos7.9,java8,java11,Jenkins2.401.3本来想装老版的jenkins,但是各种插件装不上,而新版的jenkins又依赖java11,但maven的打包又是java8的,所以必须要折腾下了步骤下载jdk8,jdk11安装包略解压改名[root@VM-4-12-opencloudoshome]#lsjdk-11.0.18_linux-x64_bin.tar.g......
  • Java中常见的网络通信模型
    目前最近仔学习RocketMQ以及Dubbo还有Spring5框架的底层部分,了解到这些技术的底层都是采用的Netty作为底层的通信的软件,于是便需要详细了解以下网络中的通信的模型以及Netty的通信模型原理。本篇是通过Redis以及Netty进行网络通信模型的逐渐演化来进行介绍,其中还会夹杂着一些比......
  • Java复制
    将一个对象的引用复制给另外一个对象,一共有三种方式。第一种是直接赋值,第二种方式是浅拷贝,第三种是深拷贝。所以大家知道了哈,这三种概念都是为了拷贝对象。直接赋值复制直接赋值。在Java中,Aa1=a2,我们需要理解的是这实际上复制的是引用,也就是说a1和a2指向的是同一个对象。因......
  • java项目更改jdk版本出现问题
    这里demo中的java版本出了点问题,将jdk17改为jdk11打开项目结构设置Project中的SDK和Languagelevel为jdk11。一、java:错误:不支持发行版本17 打开settings中的JavaCompiler修改module中项目的java版本二、java:错误:无效的源发行版:17 打开项目结构,找到下图界面修改J......
  • java面试题带链接
    下面是在网下下载的,不知道哪来的了,发出来方便查看一、Java基础1.String类为什么是final的。https://www.zhihu.com/question/313455922.HashMap的源码,实现原理,底层结构。get(key)方法时获取key的hash值,计算hash&(n-1)得到在链表数组中的位置first=tab[hash&(n-1)],先判断first的k......
  • 要实现Java中的Excel导入导出功能,可以使用Apache POI库。
    下面是一个简单的示例:导入Excel文件:importorg.apache.poi.ss.usermodel.*;importorg.apache.poi.xssf.usermodel.XSSFWorkbook;importjava.io.FileInputStream;importjava.io.IOException;publicclassExcelImportExample{publicstaticvoidmain(String[]args){......