写点什么

☕【Java 技术指南】「TestNG 专题」单元测试框架之 TestNG 使用教程指南(上)

发布于: 3 小时前
☕【Java技术指南】「TestNG专题」单元测试框架之TestNG使用教程指南(上)

TestNG 介绍

TestNG 是 Java 中的一个测试框架, 类似于 JUnit 和 NUnit, 功能都差不多, 只是功能更加强大,使用也更方便。详细使用说明请参考官方链接:https://testng.org/doc/index.html

TestNG 安装

<dependency>      <groupId>org.testng</groupId>      <artifactId>testng</artifactId>      <version>6.10</version>      <scope>test</scope></dependency>
复制代码

TestNG 的优点

  • 漂亮的 HTML 格式测试报告

  • 支持并发测试

  • 参数化测试更简单

  • 支持输出日志

  • 支持更多功能的注解

编写 TestNG 测试用例的步骤

  • 使用 Eclipse 生成 TestNG 的测试程序框架

  • 在生成的程序框架中编写测试代码逻辑

  • 根据测试代码逻辑,插入 TestNG 注解标签

  • 配置 Testng.xml 文件,设定测试类、测试方法、测试分组的执行信息

  • 执行 TestNG 的测试程序

TestNG 的简单用例

Java 直接运行
package com.demo.test.testng;import org.testng.annotations.Test;public class NewTest {  @Test  public void testFunction() {      System.out.println("this is new test");      Assert.assertTrue(true);  }}
复制代码
xml 方式运行

由于我将 xml 放置在其他文件夹,不和 class 放在一个文件夹,所以需要修改 xml,如下所示:


<?xml version="1.0" encoding="UTF-8"?><suite name="Suite" parallel="false">  <test name="Test">    <classes>      <class name="com.demo.test.testng.NewTest"/>    </classes>  </test> <!-- Test --></suite> <!-- Suite -->
复制代码

TestNG 的注解

TestNG 支持多种注解,可以进行各种组合,如下进行简单的说明

@BeforeSuite > @BeforeTest > @BeforeMethod > @Test > @AfterMethod > @AfterTest > @AfterSuite


如上列表中的 @Factory、@Linsteners 这两个是不常用的;前十个注解看起来不太容易区分,顺序不太容易看明白,以如下范例做简单说明,代码


import org.testng.Assert;import org.testng.annotations.AfterClass;import org.testng.annotations.AfterGroups;import org.testng.annotations.AfterMethod;import org.testng.annotations.AfterSuite;import org.testng.annotations.AfterTest;import org.testng.annotations.BeforeClass;import org.testng.annotations.BeforeGroups;import org.testng.annotations.BeforeMethod;import org.testng.annotations.BeforeSuite;import org.testng.annotations.BeforeTest;import org.testng.annotations.Test;
public class NewTest {
@Test(groups="group1") public void test1() { System.out.println("test1 from group1"); Assert.assertTrue(true); } @Test(groups="group1") public void test11() { System.out.println("test11 from group1"); Assert.assertTrue(true); } @Test(groups="group2") public void test2() { System.out.println("test2 from group2"); Assert.assertTrue(true); } @BeforeTest public void beforeTest() { System.out.println("beforeTest"); } @AfterTest public void afterTest() { System.out.println("afterTest"); } @BeforeClass public void beforeClass() { System.out.println("beforeClass"); } @AfterClass public void afterClass() { System.out.println("afterClass"); } @BeforeSuite public void beforeSuite() { System.out.println("beforeSuite"); } @AfterSuite public void afterSuite() { System.out.println("afterSuite"); } //只对group1有效,即test1和test11 @BeforeGroups(groups="group1") public void beforeGroups() { System.out.println("beforeGroups"); } //只对group1有效,即test1和test11 @AfterGroups(groups="group1") public void afterGroups() { System.out.println("afterGroups"); } @BeforeMethod public void beforeMethod() { System.out.println("beforeMethod"); } @AfterMethod public void afterMethod() { System.out.println("afterMethod"); }}
复制代码
运行结果如下:
beforeSuitebeforeTestbeforeClassbeforeGroupsbeforeMethodtest1 from group1afterMethodbeforeMethodtest11 from group1afterMethodafterGroupsbeforeMethodtest2 from group2afterMethodafterClassafterTestPASSED: test1PASSED: test11PASSED: test2===============================================    Default test    Tests run: 3, Failures: 0, Skips: 0===============================================afterSuite
复制代码
如何创建 TestNG 测试集合?
  • 在自动化测试的执行过程中,通常会产生批量运行多个测试用例的需求,此需求称为运行测试集合(Test Suite)

  • TestNG 的测试用例可以是相互独立的,也可以按照特定的顺序来执行(配置 TestNG.xml)

如何配置 testNG.xml 文件?
<suite name = "TestNG Suite">    //自定义的测试集合名称  <test name = "test1">    //自定义的测试名称    <classes>    //定义被运行的测试类      <class name = "cn.gloryroad.FirstTestNGDemo" />    //测试类的路径      <class name = "cn.gloryroad.NewTest" />    </classes>  </test> </suite>
复制代码
测试用例的分组(group)

执行组分组配置如下:


<suite name = "TestNG Suite">  <test name = "Grouping">    <groups>      <run>        <include name = "动物" />      </run>    </groups>    <classes>      <class name = "cn.gloryroad.Grouping"/>    </classes>  </test></suite>
复制代码
执行多组分组时配置如下(两种形式都可以):
<suite name = "TestNG Suite">  <test name = "Grouping">    <groups>      <run>        <include name = "动物" />   //name分组名称             <include name = "人" />      </run>    </groups>    <classes>      <class name = "cn.gloryroad.Grouping"/>    </classes>  </test></suite>
复制代码

依赖测试(dependsOnMethod)

被依赖的方法优先于此方法执行


@Test(dependsOnMethod = {"方法名称"})
复制代码


特定顺序执行测试用例(priority)


按照数字大小顺序优先执行,优先执行 1,然后是 2…


@Test(priority = 0/1/2/3/4/…)
复制代码


如何跳过某个测试方法(enabled = false)


@Test(priority = 0/1… , enabled = false)
复制代码


执行结束后,在测试报告中显示跳过的测试用例数,例如 skip=1



创建测试案例类

  • 创建一个 Java 测试类 ParameterizedTest1.java.

  • 测试方法 parameterTest()添加到测试类。此方法需要一个字符串作为输入参数。

  • 添加注释 @Parameters("myName") 到此方法。该参数将被传递 testng.xml,在下一步我们将看到一个值。


创建 Java 类文件名 ParameterizedTest1.java


import org.testng.annotations.Parameters;import org.testng.annotations.Test; public class ParameterizedTest1 {      @Test      @Parameters("myName")      public void parameterTest(String myName) {          System.out.println("Parameterized value is : " + myName);      }}
复制代码

创建 TESTNG.XML

创建 testng.xml C:\ > TestNG_WORKSPACE 执行测试案例


<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >  <suite name="Suite1">      <test name="test1">      <parameter name="myName" value="manisha"/>      <classes>          <class name="ParameterizedTest1" />          </classes>      </test>  </suite>
复制代码


我们还可以定义参数在<suite>级别。假设我们已经定义在两个<suite>和<test>级别 myName,在这种情况下,常规的作用域规则适用。这意味着,任何类里面<test>标签将查看值参数定义在<test>,而 testng.xml 文件中的类的其余部分将看到定义在<suite>中值

编译使用 javac 的测试用例类。
javac ParameterizedTest1.java
复制代码


现在,运行 testng.xml,其中将运行 parameterTest 方法。TestNG 的将试图找到一个命名 myName 的第一<test>标签的参数,然后,如果它不能找到它,它会搜索包围在的<suit>标签。

验证输出。

Parameterized value is : manisha ===============================================Suite1Total tests run: 1, Failures: 0, Skips: 0===============================================
复制代码
数据驱动(@DataProvider)
  • 当你需要通过复杂的参数或参数需要创建从 Java(复杂的对象,对象读取属性文件或数据库等..),在这种情况下,可以将参数传递使用数据提供者。数据提供者 @DataProvider 的批注的方法。

  • 这个注解只有一个字符串属性:它的名字。如果不提供名称,数据提供者的名称会自动默认方法的名称。数据提供者返回一个对象数组。


让我们看看下面的例子使用数据提供者。第一个例子是 @DataProvider 的使用 Vector,String 或 Integer 作为参数,第二个例子是关于 @DataProvider 的使用对象作为参数。


实例 1


在这里 @DataProvider 通过整数和布尔参数。


创建 Java 类


创建一个 java 类 PrimeNumberChecker.java。这个类检查,如果是素数。


  public class PrimeNumberChecker {      public Boolean validate(final Integer primeNumber) {          for (int i = 2; i < (primeNumber / 2); i++) {              if (primeNumber % i == 0) {                  return false;               }          }          return true;      }  }
复制代码


创建测试案例类


  • 创建一个 Java 测试类 ParamTestWithDataProvider1.java.

  • 定义方法 primeNumbers(),其定义为 DataProvider 使用注释。此方法返回的对象数组的数组。

  • 测试方法 testPrimeNumberChecker()添加到测试类中。此方法需要一个整数和布尔值作为输入参数。这个方法验证,如果传递的参数是一个素数。

  • 添加注释 @Test(dataProvider = "test1") 到此方法。dataProvider 的属性被映射到"test1".


创建 Java 类文件名 ParamTestWithDataProvider1.java


  import org.testng.Assert;  import org.testng.annotations.BeforeMethod;  import org.testng.annotations.DataProvider;  import org.testng.annotations.Test;   public class ParamTestWithDataProvider1 {
   private PrimeNumberChecker primeNumberChecker;
   @BeforeMethod    public void initialize() {    primeNumberChecker = new PrimeNumberChecker();    }   @DataProvider(name = "test1")    public static Object[][] primeNumbers() {   return new Object[][] { { 2, true }, { 6, false }, { 19, true },      { 22, false }, { 23, true } };   }    // This test will run 4 times since we have 5 parameters defined   @Test(dataProvider = "test1")    public void testPrimeNumberChecker(Integer inputNumber,   Boolean expectedResult) {    System.out.println(inputNumber + " " + expectedResult);    Assert.assertEquals(expectedResult,   primeNumberChecker.validate(inputNumber));   }  }
复制代码


创建 TESTNG.XML


<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >  <suite name="Suite1">      <test name="test1">      <classes>          <class name="ParamTestWithDataProvider1" />          </classes>      </test>  </suite>
复制代码


运行 testng.xml.


验证输出。 2 true6 false19 true22 false23 true ===============================================Suite1Total tests run: 5, Failures: 0, Skips: 0===============================================
复制代码
实例 2

在这里,@DataProvider 传递对象作为参数。


创建 Java 类


创建一个 Java 类 Bean.java, 对象带有 get/set 方法


public class Bean {    private String val;    private int i;    public Bean(String val, int i){        this.val=val;        this.i=i;    }    public String getVal() {    return val;    }    public void setVal(String val) {    this.val = val;    }    public int getI() {    return i;    }    public void setI(int i) {    this.i = i;    }}
复制代码
创建测试案例类
  • 创建一个 Java 测试类 ParamTestWithDataProvider2.java.

  • 定义方法 primeNumbers(),其定义为 DataProvider 使用注释。此方法返回的对象数组的数组。

  • 添加测试类中测试方法 TestMethod()。此方法需要对象的 bean 作为参数。

  • 添加注释 @Test(dataProvider = "test1") 到此方法. dataProvider 属性被映射到 "test1".


创建 Java 类文件名 ParamTestWithDataProvider2.java


  import org.testng.annotations.DataProvider;  import org.testng.annotations.Test;   public class ParamTestWithDataProvider2 {      @DataProvider(name = "test1")      public static Object[][] primeNumbers() {          return new Object[][] { { new Bean("hi I am the bean", 111) } };      }       @Test(dataProvider = "test1")      public void testMethod(Bean myBean) {          System.out.println(myBean.getVal() + " " + myBean.getI());      }  }
复制代码


创建 TESTNG.XML


<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >  <suite name="Suite1">      <test name="test1">      <classes>          <class name="ParamTestWithDataProvider2" />          </classes>      </test>  </suite>
复制代码


运行 testng.xml.


hi I am the bean 111 ===============================================Suite1Total tests run: 1, Failures: 0, Skips: 0===============================================
复制代码


测试报告中自定义日志(Reporter.log(“输入自定义内容”)),例如:


  @Test(groups = {"人"})
  public void student(){
    System.out.println("学生方法被调用");
    Reporter.log("学生方法自定义日志");
  }
复制代码



测试方法使用大全

TestNG 预期异常测试

预期异常测试通过在 @Test 注解后加入预期的 Exception 来进行添加,范例如下所示:


@Test(expectedExceptions = ArithmeticException.class)    public void divisionWithException() {        int i = 1 / 0;        System.out.println("After division the value of i is :"+ i);    }
复制代码


运行结果如下:


[RemoteTestNG] detected TestNG version 6.10.0[TestNG] Running:  C:\Users\Administrator\AppData\Local\Temp\testng-eclipse--754789457\testng-customsuite.xml
PASSED: divisionWithException
=============================================== Default test Tests run: 1, Failures: 0, Skips: 0===============================================

===============================================Default suiteTotal tests run: 1, Failures: 0, Skips: 0===============================================
[TestNG] Time taken by org.testng.reporters.JUnitReportReporter@55d56113: 0 ms[TestNG] Time taken by org.testng.reporters.SuiteHTMLReporter@1e127982: 0 ms[TestNG] Time taken by org.testng.reporters.jq.Main@6e0e048a: 32 ms[TestNG] Time taken by [FailedReporter passed=0 failed=0 skipped=0]: 0 ms[TestNG] Time taken by org.testng.reporters.XMLReporter@43814d18: 0 ms[TestNG] Time taken by org.testng.reporters.EmailableReporter2@6ebc05a6: 0 ms
复制代码
TestNG 忽略测试

有时候我们写的用例没准备好,或者该次测试不想运行此用例,那么删掉显然不明智,那么就可以通过注解 @Test(enabled = false)来将其忽略掉,此用例就不会运行了,如下范例:


import org.testng.annotations.Test;
public class TestCase1 {
@Test(enabled=false) public void TestNgLearn1() { System.out.println("this is TestNG test case1"); } @Test public void TestNgLearn2() { System.out.println("this is TestNG test case2"); }}
复制代码
运行结果:
this is TestNG test case2PASSED: TestNgLearn2
复制代码

TestNG 超时测试

“超时”表示如果单元测试花费的时间超过指定的毫秒数,那么 TestNG 将会中止它并将其标记为失败。此项常用于性能测试。如下为一个范例:


import org.testng.annotations.Test;
public class TestCase1 {
@Test(timeOut = 5000) // time in mulliseconds public void testThisShouldPass() throws InterruptedException { Thread.sleep(4000); }
@Test(timeOut = 1000) public void testThisShouldFail() { while (true){ // do nothing }
}}
复制代码
结果如下:
PASSED: testThisShouldPassFAILED: testThisShouldFailorg.testng.internal.thread.ThreadTimeoutException: Method com.demo.test.testng.TestCase1.testThisShouldFail() didn't finish within the time-out 1000  at com.demo.test.testng.TestCase1.testThisShouldFail(TestCase1.java:37)  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)  at java.lang.reflect.Method.invoke(Method.java:498)  at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:104)  at org.testng.internal.InvokeMethodRunnable.runOne(InvokeMethodRunnable.java:54)  at org.testng.internal.InvokeMethodRunnable.run(InvokeMethodRunnable.java:44)  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)  at java.util.concurrent.FutureTask.run(FutureTask.java:266)  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)  at java.lang.Thread.run(Thread.java:748)
复制代码

未完待续

后续会进行详细的介绍使用

发布于: 3 小时前阅读数: 5
用户头像

🏆2021年InfoQ写作平台-签约作者 🏆 2020.03.25 加入

👑【酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“】 🏅 【Java技术领域,MySQL技术领域,APM全链路追踪技术及微服务、分布式方向的技术体系等】 我们始于迷惘,终于更高水平的迷惘

评论

发布
暂无评论
☕【Java技术指南】「TestNG专题」单元测试框架之TestNG使用教程指南(上)