☕【Java技術指南】「TestNG專題」單元測試框架之TestNG使用教程指南(下)

李浩宇Alex發表於2021-08-29

承接上文

☕【Java技術指南】「TestNG專題」單元測試框架之TestNG使用教程指南(上),繼續開展我們的下篇的TestNG的技術特性。

分組測試

分組測試即為使用group,如果你使用xml的話就是裡邊的標籤,如果是直接在class中,是通過@Test(groups="group2")這種方式來分組,如第四節的註解說明中的那個例子,分了兩個group,而且@BeforeGroup是需要新增group名稱才可以正確掛載到該group下的。

這個group說明可以是在單個的測試方法上,也可以在class上,只要具有同樣的group名稱都會在同一個group中,同時group名稱可以有多個,類似@Test(groups = {"mysql","database"})這種,範例如下:

一個測試檔案NewTest.class:

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");
  }
  
  @BeforeGroups(groups="group1")
  public void beforeGroups() 
  {
	  System.out.println("beforeGroups");
  }
  
  @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");
  }
}
另一個TestCase1.class:
@Test(groups= "group2")
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");
    }
}
xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<suite name="Suite" parallel="false">
  <test name="Test">
    <groups>
      <include name="group1"></incloud>
      <include name="group2"></incloud>
    </groups>
    <classes>
      <class name="com.demo.test.testng.NewTest"/>
      <class name="com.demo.test.testng.TestCase1"/>
    </classes>
  </test> <!-- Test -->
</suite> <!-- Suite -->
執行結果如下:
beforeSuite
beforeTest
beforeClass
beforeGroups
beforeMethod
test1 from group1
afterMethod
beforeMethod
test11 from group1
afterMethod
afterGroups
beforeMethod
test2 from group2
afterMethod
afterClass
this is TestNG test case2
afterTest
afterSuite

如上所示,先執行了group1的兩個用例,再執行group2的兩條用例;
注意在xml標識group,需要將要執行的group加進來,同時還要將被標識這些group的class也加進來,不被加進去的不會執行;

分suite測試

  • 測試套件是用於測試軟體程式的行為或一組行為的測試用例的集合。 在TestNG中,我們無法在測試原始碼中定義一個套件,但它可以由一個XML檔案表示,因為套件是執行的功能。 它還允許靈活配置要執行的測試。

  • 套件可以包含一個或多個測試,並由標記定義。是testng.xml的根標記。 它描述了一個測試套件,它又由幾個部分組成。

下表列出了接受的所有定義的合法屬性。

依賴測試

可能需要以特定順序呼叫測試用例中的方法,或者可能希望在方法之間共享一些資料和狀態。 TestNG支援這種依賴關係,因為它支援在測試方法之間顯式依賴的宣告。

TestNG允許指定依賴關係:
  • 在@Test註釋中使用屬性dependsOnMethods
  • 在@Test註釋中使用屬性dependsOnGroups

除此之外依賴還分為hard依賴和soft依賴:

  • hard依賴:預設為此依賴方式,即其所有依賴的methods或者groups必須全部pass,否則被標識依賴的類或者方法將會被略過,在報告中標識為skip,如後面的範例所示,此為預設的依賴方式;
  • soft依賴:此方式下,其依賴的方法或者組有不是全部pass也不會影響被標識依賴的類或者方法的執行,注意如果使用此方式,則依賴者和被依賴者之間必須不存在成功失敗的因果關係,否則會導致用例失敗。
  • 此方法在註解中需要加入alwaysRun=true即可,如@Test(dependsOnMethods= {"TestNgLearn1"}, alwaysRun=true);

在TestNG中,我們使用dependOnMethods和dependsOnGroups來實現依賴測試。 且這兩個都支援正規表示式,如範例三所示,如下為幾個使用範例

範例一,被依賴方法pass:
public class TestCase1 {
    @Test(enabled=true)
    public void TestNgLearn1() {
        System.out.println("this is TestNG test case1");
    }
    @Test(dependsOnMethods= {"TestNgLearn1"})
    public void TestNgLearn2() {
        System.out.println("this is TestNG test case2");
    }
}
執行結果:
this is TestNG test case1
this is TestNG test case2
PASSED: TestNgLearn1
PASSED: TestNgLearn2
範例二,被依賴方法fail:
public class TestCase1 {
    @Test(enabled=true)
    public void TestNgLearn1() {
        System.out.println("this is TestNG test case1");
        Assert.assertFalse(true);
    }
    @Test(dependsOnMethods= {"TestNgLearn1"})
    public void TestNgLearn2() {
        System.out.println("this is TestNG test case2");
    }
}
結果:
this is TestNG test case1
FAILED: TestNgLearn1
junit.framework.AssertionFailedError
	at junit.framework.Assert.fail(Assert.java:47)
	at junit.framework.Assert.assertTrue(Assert.java:20)
	at junit.framework.Assert.assertFalse(Assert.java:34)
	at junit.framework.Assert.assertFalse(Assert.java:41)
	at com.demo.test.testng.TestCase1.TestNgLearn1(TestCase1.java:26)
	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.Invoker.invokeMethod(Invoker.java:645)
	at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:851)
	at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1177)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:129)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:112)
	at org.testng.TestRunner.privateRun(TestRunner.java:756)
	at org.testng.TestRunner.run(TestRunner.java:610)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:387)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:382)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:340)
	at org.testng.SuiteRunner.run(SuiteRunner.java:289)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1293)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1218)
	at org.testng.TestNG.runSuites(TestNG.java:1133)
	at org.testng.TestNG.run(TestNG.java:1104)
	at org.testng.remote.AbstractRemoteTestNG.run(AbstractRemoteTestNG.java:114)
	at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:251)
	at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:77)

SKIPPED: TestNgLearn2
範例三、group依賴:

如下所示,method1依賴group名稱為init的所有方法:

@Test(groups = { "init" })
public void serverStartedOk() {}
 
@Test(groups = { "init" })
public void initEnvironment() {}
 
@Test(dependsOnGroups = { "init.*" })
public void method1() {}

這裡init這個group中的兩個方法的執行順序如果沒有在xml中指明則每次執行的順序不能保證

引數化測試

TestNG中的另一個有趣的功能是引數化測試。 在大多數情況下,您會遇到業務邏輯需要大量測試的場景。 引數化測試允許開發人員使用不同的值一次又一次地執行相同的測試。

TestNG可以通過兩種不同的方式將引數直接傳遞給測試方法:

使用testng.xml
使用資料提供者

下面分別介紹兩種傳參方式:

  1. 使用textng.xml傳送引數
範例程式碼如下:
public class TestCase1 {
    @Test(enabled=true)
    @Parameters({"param1", "param2"})
    public void TestNgLearn1(String param1, int param2) {
        System.out.println("this is TestNG test case1, and param1 is:"+param1+"; param2 is:"+param2);
        Assert.assertFalse(false);
    }
    @Test(dependsOnMethods= {"TestNgLearn1"})
    public void TestNgLearn2() {
        System.out.println("this is TestNG test case2");
    }
}
xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<suite name="Suite" parallel="false">
  <test name="Test">
    <parameter name="param1" value="1011111" />
    <parameter name="param2" value="10" />
    <classes>
      <class name="com.demo.test.testng.TestCase1"/>
    </classes>
  </test> <!-- Test -->
</suite> <!-- Suite -->
執行xml,結果如下:
this is TestNG test case1, and param1 is:1011111; param2 is:10
this is TestNG test case2

===============================================
Suite
Total tests run: 2, Failures: 0, Skips: 0
===============================================
使用@DataProvider傳遞引數

此處需要注意,傳參的型別必須要一致,且帶有@DataProvider註解的函式返回的必然是Object[][],此處需要注意。

程式碼如下:
public class TestCase1 {
    @DataProvider(name = "provideNumbers")
    public Object[][] provideData() {
        return new Object[][] { { 10, 20 }, { 100, 110 }, { 200, 210 } };
    }
    @Test(dataProvider = "provideNumbers")
    public void TestNgLearn1(int param1, int param2) {
        System.out.println("this is TestNG test case1, and param1 is:"+param1+"; param2 is:"+param2);
        Assert.assertFalse(false);
    }
    @Test(dependsOnMethods= {"TestNgLearn1"})
    public void TestNgLearn2() {
        System.out.println("this is TestNG test case2");
    }
}
執行此class,結果為:
this is TestNG test case1, and param1 is:10; param2 is:20
this is TestNG test case1, and param1 is:100; param2 is:110
this is TestNG test case1, and param1 is:200; param2 is:210
this is TestNG test case2
PASSED: TestNgLearn1(10, 20)
PASSED: TestNgLearn1(100, 110)
PASSED: TestNgLearn1(200, 210)
PASSED: TestNgLearn2

XML配置檔案說明

前面講的大多都是以測試指令碼為基礎來執行的,少部分是以xml執行,這裡以xml來講解下:

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="SuiteName" verbose="1" > 
如下分別講解各個標籤:
suite標籤

testNG.xml檔案的最外層標籤即suite,即測試套件,其下可以有多個,其有幾個可以新增的屬性在第十節的分suite測試中有做說明,這裡做下詳細說明

  1. name屬性

此屬性屬於必須要有的,值可以自行設定,此名字會在testNG的報告中看到;

  1. verbose屬性

此屬性為指定testNG報告的詳細程度,從0開始到10,其中10為最詳細,預設生成的xml此屬性值為1;

  1. parallel屬性
  • 此屬性是指代執行方式,預設為none,即序列執行方式;並行執行方法包括如下幾種,下面做分別說明:
    • methods:方法層級,若為此值,則該suite下所有的測試方法都將進行多執行緒,即測試用例級別的多執行緒。如果用例之間有依賴,則執行順序會按照設定的依賴來執行;
    <suite name="My suite" parallel="methods" thread-count="5">
    
    • tests:TestNG將在同一執行緒中執行相同的標籤中的所有方法,每個標籤都將處於一個單獨的執行緒中,這允許您將不是執行緒安全的所有類分組在同一個中,並保證它們都將在同一個執行緒中執行,同時利用TestNG使用盡可能多的執行緒執行測試。
    <suite name="My suite" parallel="tests" thread-count="5">
    
    • classes:類級別併發,即TestNG會將該suite下每個class都將在單獨的執行緒中執行,同一個class下的所有用例都將在同一個執行緒中執行;
    <suite name="My suite" parallel="classes" thread-count="5">
    
    • instances:例項級別,即TestNG將在同一執行緒中執行同一例項中的所有方法,兩個不同例項上的兩個方法將在不同的執行緒中執行。
    <suite name="My suite" parallel="instances" thread-count="5">
    
  1. thread-count屬性

此屬性用於指定執行緒數,按照需要輸入,需要parallel引數非none時才可以新增;

  1. annotations屬性

此項為註解的級別,為methods級別和class級別,一般不用設定;

  1. time-out屬性

此屬性用於指定超時時間,該suite下所有的用例的超時時間;

  1. group-by-instances屬性

此項用於那些有依賴的方法,且被依賴的物件有多個過載物件,因為如果是依賴方法,且該方法有多個過載方法,則預設是會將所有過載方法都跑完再執行被依賴方法,但有時候我們不想這樣,則將此項設定為true即可;

  1. preserve-order屬性

值可輸入true或者false,如果為true,則用例執行會按照在xml中的順序執行,否則會亂序執行,不新增此屬性的話預設是按順序執行的;

2、test標籤

此標籤無特別意義,其下可以包括多個標籤,如groups、classes等,如下介紹下幾種書寫方式:

選擇一個包中的全部測試指令碼(包含子包)

<test name = "allTestsInAPackage" >
   <packages>
      <package name = "whole.path.to.package.* />
   </packages>
</test>

選擇一個類中的全部測試指令碼

<test name = "allTestsInAClass" >
   <classes>
  <class name="whole.path.to.package.className />
   </classes>
</test>

選擇一個類中的部分測試指令碼

<test name = "aFewTestsFromAClass" >
   <classes>
  <class name="whole.path.to.package.className >
      <methods>
         <include name = "firstMethod" />
         <include name = "secondMethod" />
         <include name = "thirdMethod" />
      </methods>
  </class>
   </classes>
</test>

選擇一個包中的某些組

<test name = "includedGroupsInAPackage" >
   <groups>
      <run>
         <include name = "includedGroup" />
      </run>
   </groups>
   <packages>
      <package name = "whole.path.to.package.* />
   </packages>
</test>

排除一個包中的某些組

<test name = "excludedGroupsInAPackage" >
   <groups>
      <run>
         <exclude name = "excludedGroup" />
      </run>
   </groups>
   <packages>
      <package name = "whole.path.to.package.* />
   </packages>
</test>
group標籤

此標籤必然是在標籤下的,用於標識那些組會被用於測試或者被排除在測試之外,其同級必然要包含一個標籤或者標籤,用於指定groups來自於哪些包或者類;
如下即為包含一個group,排除一個group的例子:

<groups>
  <run>
     <include name = "includedGroupName" />
     <exclude name = "excludedGroupName" />
  </run>
</groups>

高階應用:

<test name="Regression1">
  <groups>
    <define name="functest">
      <include name="windows"/>
      <include name="linux"/>
    </define>
  
    <define name="all">
      <include name="functest"/>
      <include name="checkintest"/>
    </define>
  
    <run>
      <include name="all"/>
    </run>
  </groups>
  
  <classes>
    <class name="test.sample.Test1"/>
  </classes>
</test>

其他

其他的話就是測試指令碼的選擇了,有三種方式:

選擇一個包

<packages>
    <package name = "packageName" />
</packages>

選擇一個類

<classes>
    <class name = "className" />
</classes>

選擇一個方法

<classes>
    <class name = "className" />
       <methods>
          <include name = "methodName" />
       </methods>
    </class>
</classes>

這裡也支援正規表示式,例如:

<test name="Test1">
  <classes>
    <class name="example1.Test1">
      <methods>
        <include name=".*enabledTestMethod.*"/>
        <exclude name=".*brokenTestMethod.*"/>
      </methods>
     </class>
  </classes>
</test>

相關文章