☕【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");
  }
}
運行結果如下:
beforeSuite
beforeTest
beforeClass
beforeGroups
beforeMethod
test1 from group1
afterMethod
beforeMethod
test11 from group1
afterMethod
afterGroups
beforeMethod
test2 from group2
afterMethod
afterClass
afterTest
PASSED: test1
PASSED: test11
PASSED: 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>

我們還可以定義參數在 級別。假設我們已經定義在兩個 級別myName,在這種情況下,常規的作用域規則適用。這意味着,任何類里面 標簽將查看值參數定義在 ,而testng.xml文件中的類的其余部分將看到定義在 中值

編譯使用javac的測試用例類。
javac ParameterizedTest1.java

現在,運行testng.xml,其中將運行parameterTest方法。TestNG的將試圖找到一個命名myName的第一 標簽的參數,然后,如果它不能找到它,它會搜索包圍在的 標簽。

驗證輸出。

Parameterized value is : manisha
 
===============================================
Suite1
Total 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 true
6 false
19 true
22 false
23 true
 
===============================================
Suite1
Total 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
 
===============================================
Suite1
Total 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 suite
Total 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 case2
PASSED: 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: testThisShouldPass
FAILED: testThisShouldFail
org.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)

未完待續

后續會進行詳細的介紹使用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM