測試框架TestNG學(xué)習(xí)筆記
一、TestNG的基本介紹和如何在maven中引用
1、適合測試人員使用的原因
1)比Junit涵蓋功能更全面的測試框架
2)Junit更適合隔離性比較強(qiáng)的單元測試
3)TestNG更適合復(fù)雜的集成測試
2、TestNG的使用
pom.xml的依賴包將信息都填寫好,maven框架將自動下載和使用。

二、TestNG基本注解與執(zhí)行順序?qū)崙?zhàn)
1、Idea創(chuàng)建一個(gè)項(xiàng)目和對應(yīng)的modle,目錄結(jié)構(gòu)如下:

2.1 注解實(shí)戰(zhàn) @Test標(biāo)簽
1、創(chuàng)建包 com.course.testng
2、創(chuàng)建類 BasicAnnotation.java
package com.course.testng;
import org.testng.annotations.Test;
public class BasicAnnotation{
// 最基本的注解,用來把方法標(biāo)記成測試用例
@Test
public void testCase1(){
System.out.println("這是測試用例1");
}
}
1)@Test 需要 alt+enter 鍵,add ‘testng’ to classpath
pom.xml文件會自動增加依賴:

2)@Test 需要導(dǎo)入包
import org.testng.annotations.Test;
3、運(yùn)行結(jié)果

@Test是最基本的注解,用來把方法標(biāo)記成測試用例
2.2 注解實(shí)戰(zhàn) BeforeMethod和AfterMethod
1、在測試方法之前運(yùn)行的標(biāo)簽 @BeforeMethod
2、在測試方法之后運(yùn)行的標(biāo)簽 @AfterMethod

3、多個(gè)測試方法執(zhí)行

說明:如果有多個(gè)測試方法,在每一個(gè)測試方法之前和之后,BeforeMethod、AfterMethod都運(yùn)行一次。
2.3 注解實(shí)戰(zhàn)BeforeClass和AfterClass
1、在測試類之前運(yùn)行的標(biāo)簽 @BeforeClass
2、在測試類之后運(yùn)行的標(biāo)簽 @AfterClass

說明:BeforeClass 和 AfterClass在類中,只運(yùn)行一次。
注冊對象,靜態(tài)方法,變量賦值等都可以用到。
2.4 注解實(shí)戰(zhàn):BeforeSuite和AfterSuit
1、BeforeSuite 在整個(gè)測試套件開始之前運(yùn)行
2、AfterSuite 在整個(gè)測試套件結(jié)束之后運(yùn)行

說明:BeforeSuit 和 AfterSuit 在類中,只運(yùn)行一次。
- 關(guān)注點(diǎn)在于執(zhí)行順序:
- beforeSuite --> beforeClass --> beforeMethod --> 測試方法1 --> afterMethod --> beforeMethod --> 測試方法2 --> afterMethod --> afterClass --> afterSuite
2.5 Before/After注解總結(jié)

代碼舉例:
package com.course.test;
import org.testng.annotations.*;
public class BasicAnnotation {
@Test
public void testCase1(){
System.out.println("這是測試用例1");
}
@Test
public void testCase2(){
System.out.println("這是測試用例2");
}
@BeforeMethod
public void beforeMethod(){
System.out.println("beforeMethod在測試方法運(yùn)行之前運(yùn)行");
}
@AfterMethod
public void afterMethod(){
System.out.println("afterMethod在測試方法運(yùn)行之后運(yùn)行");
}
@BeforeClass
public void beforeClass(){
System.out.println("beforeClass在類運(yùn)行之前運(yùn)行");
}
@AfterClass
public void afterClass(){
System.out.println("afterClass在類運(yùn)行之后運(yùn)行");
}
@BeforeSuite
public void beforeSuite(){
System.out.println("beforeSuite在類運(yùn)行之前運(yùn)行");
}
@AfterSuite
public void afterSuite(){
System.out.println("afterSuite在類運(yùn)行之后運(yùn)行");
}
}
結(jié)果:
beforeSuite在類運(yùn)行之前運(yùn)行
beforeClass在類運(yùn)行之前運(yùn)行
beforeMethod在測試方法運(yùn)行之前運(yùn)行
這是測試用例1
afterMethod在測試方法運(yùn)行之后運(yùn)行
beforeMethod在測試方法運(yùn)行之前運(yùn)行
這是測試用例2
afterMethod在測試方法運(yùn)行之后運(yùn)行
afterClass在類運(yùn)行之后運(yùn)行
afterSuite在類運(yùn)行之后運(yùn)行
三、套件測試
測試套件是用于測試軟件程序的行為或一組行為的測試用例的集合。在TestNG中,我們無法定義一個(gè)套件,但它可以由一個(gè)XML文件表示,因?yàn)樘准菆?zhí)行的功能。它還允許靈活配置要運(yùn)行的測試。 套件可以包含一個(gè)或多個(gè)測試,并由
是testng.xml的根標(biāo)記。 它描述了一個(gè)測試套件,它又由幾個(gè) 部分組成。 接收的所有定義的合法屬性。
![]()
3.1 創(chuàng)建suite包
創(chuàng)建類:suiteConfig.java、LoginTest.java、PayTest.java
package com.course.test.suite;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
// 測試套件之前需要運(yùn)行的方法
public class SuiteConfig {
@BeforeSuite
public void beforeSuite(){
System.out.println("before Suite運(yùn)行了");
}
@AfterSuite
public void afterSuite(){
System.out.println("after Suite運(yùn)行了");
}
@BeforeTest
public void beforeTest(){
System.out.println("beforeTest");
}
@AfterTest
public void afterTest(){
System.out.println("afterTest");
}
}
package com.course.test.suite;
import org.testng.annotations.Test;
// 寫測試類
public class LoginTest {
@Test
public void loginTaobao(){
System.out.println("淘寶登錄成功!");
}
}
package com.course.test.suite;
import org.testng.annotations.Test;
public class PayTest {
@Test
public void paySuccess(){
System.out.println("淘寶支付成功!");
}
}
3.2 在 resources中創(chuàng)建suite.xml
目錄:src/main/resources/suite.xml
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="test">
<test name="login">
<classes>
<class name="com.course.test.suite.SuiteConfig" />
<class name="com.course.test.suite.LoginTest"/>
</classes>
</test>
<test name="pay">
<classes>
<class name="com.course.test.suite.SuiteConfig" />
<class name="com.course.test.suite.PayTest"/>
</classes>
</test>
</suite>
- 說明:使用suite.xml文件,該文件用來管理測試用例,并與運(yùn)行testNG。
套件就是將所有測試類整理在一起,形成一套測試用例
測試集是指測試模塊,一般一個(gè)項(xiàng)目可以按照模塊分幾部分,即不同的test
測試集下的所有測試類
具體測試類,name 屬性指定測試類的路徑
測試類下具體的測試方法,如果不寫此標(biāo)簽,則默認(rèn)包含測試類下的所有方法
3.3 運(yùn)行suite.xml文件
運(yùn)行結(jié)果:

說明:
測試套件test中,有2個(gè)測試模塊login和pay。
login模塊下的測試類是 SuiteConfig.java和LoginTest.java,執(zhí)行該測試類下所有方法。
pay模塊下的測試類是 SuiteConfig.java和PayTest.java,執(zhí)行該測試類下所有方法。
由于suite.xml只有一個(gè)測試套件test,所以beforeSuite()/afterSuite()只運(yùn)行一次。beforeTest()/afterTest()針對每個(gè)測試方法都執(zhí)行一次。測試方法 loginTaobao()、paySuccess() 分別執(zhí)行一次。
四、忽略測試
1、什么時(shí)忽略測試:本次測試執(zhí)行不想執(zhí)行該用例,或者編寫的代碼沒有準(zhǔn)備就緒,并且測試用例要測試該方法/代碼是否成功或失敗。
使用注釋 @Test(enable=false) 禁用此測試用例,繞過該測試用例。
2、新建 IgnoreTest.java
package com.course.testng;
import org.testng.annotations.Test;
public class IgnoreTest {
@Test
public void ignore1(){
System.out.println("ignore1 執(zhí)行!");
}
@Test(enabled = false)
public void ignore2(){
System.out.println("ignore2 執(zhí)行!");
}
@Test(enabled = true)
public void ignore3(){
System.out.println("ignore3 執(zhí)行!");
}
}
執(zhí)行結(jié)果:
ignore1 執(zhí)行!
ignore3 執(zhí)行!
說明: @Test(enabled = false) 的用例會被忽略
五、分組測試
分組測試允許你將方法調(diào)度到適當(dāng)?shù)牟糠郑?zhí)行復(fù)雜的測試方法分組。
它不僅可以聲明屬于某個(gè)分組的方法,還可以指定包含其他組的組。然后調(diào)用 TestNG,并要求其包含一組特定的組(或正則表達(dá)式),同時(shí)排除另一個(gè)分組。
組測試提供了如何分區(qū)測試的最大靈活性,如果您想要背靠背運(yùn)行兩組不同的測試,則不需要重新編譯任何內(nèi)容。
使用
5.1 分組測試-方法分組測試
1、新建組groups,新建類 GroupsOnMethod.java
package com.course.testng.groups;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
public class GroupsOnMethod {
@Test(groups = "server")
public void test1(){
System.out.println("這是服務(wù)端組的測試方法1111");
}
@Test(groups = "server")
public void test2(){
System.out.println("這是服務(wù)端組的測試方法2222");
}
@Test(groups = "client")
public void test3(){
System.out.println("這是客戶端組的測試方法3333");
}
@Test(groups = "client")
public void test4(){
System.out.println("這是客戶端組的測試方法4444");
}
@BeforeGroups("server")
public void beforeGroupsOnServer(){
System.out.println("這是服務(wù)端組運(yùn)行之前運(yùn)行的方法:beforeGroupsOnServer");
}
@AfterGroups("server")
public void afterGroupsOnServer(){
System.out.println("這是服務(wù)端組運(yùn)行之后運(yùn)行的方法:afterGroupsOnServer");
}
@BeforeGroups("client")
public void beforeGroupsOnClient(){
System.out.println("這是客戶端組運(yùn)行之前運(yùn)行的方法:beforeGroupsOnClient");
}
@AfterGroups("client")
public void afterGroupsOnClient(){
System.out.println("這是客戶端組運(yùn)行之后運(yùn)行的方法:afterGroupsOnClient");
}
}
結(jié)果:
這是服務(wù)端組運(yùn)行之前運(yùn)行的方法:beforeGroupsOnServer
這是服務(wù)端組的測試方法1111
這是服務(wù)端組的測試方法2222
這是服務(wù)端組運(yùn)行之后運(yùn)行的方法:afterGroupsOnServer
這是客戶端組運(yùn)行之前運(yùn)行的方法:beforeGroupsOnClient
這是客戶端組的測試方法3333
這是客戶端組的測試方法4444
這是客戶端組運(yùn)行之后運(yùn)行的方法:afterGroupsOnClient
說明:
- 方法test1()、test2() 屬于分組
server,beforeGroupsOnServer()在server組的方法執(zhí)行之前運(yùn)行,afterGroupsOnServer()在server組的方法執(zhí)行之后運(yùn)行; - 方法test3()、test4() 屬于分組
client,beforeGroupsOnClient()在server組的方法執(zhí)行之前運(yùn)行,afterGroupsOnClient()在client組的方法執(zhí)行之后運(yùn)行。
5.2 分組測試中-類分組測試
1、新建組:groups,新建類 GroupsOnClass1.java、 GroupsOnClass2.java、GroupsOnClass3.java
其中 GroupsOnClass1.java和GroupsOnClass2.java是一個(gè)分組stu;GroupsOnClass3.java是一個(gè)分組teacher。
package com.course.testng.groups;
import org.testng.annotations.Test;
@Test(groups = "stu")
public class GroupsOnClass1 {
public void stu1(){
System.out.println("GroupsOnClass1 中的stu1111運(yùn)行");
}
public void stu2(){
System.out.println("GroupsOnClass1 中的stu2222運(yùn)行");
}
}
package com.course.testng.groups;
import org.testng.annotations.Test;
@Test(groups = "stu")
public class GroupsOnClass2 {
public void stu1(){
System.out.println("GroupsOnClass2 中的stu1111運(yùn)行");
}
public void stu2(){
System.out.println("GroupsOnClass2 中的stu2222運(yùn)行");
}
}
package com.course.testng.groups;
import org.testng.annotations.Test;
@Test(groups = "teacher")
public class GroupsOnClass3 {
public void teacher1(){
System.out.println("GroupsOnClass3 中的teacher1111運(yùn)行");
}
public void teacher2(){
System.out.println("GroupsOnClass3 中的teacher2222運(yùn)行");
}
}
2、新建 groupsOnClass.xml
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="suitename">
<test name="runAll">
<classes>
<class name="com.course.testng.groups.GroupsOnClass1"/>
<class name="com.course.testng.groups.GroupsOnClass2"/>
<class name="com.course.testng.groups.GroupsOnClass3"/>
</classes>
</test>
<test name="OnlyRunStu">
<groups>
<run>
<include name="stu"/>
</run>
</groups>
<classes>
<class name="com.course.testng.groups.GroupsOnClass1"/>
<class name="com.course.testng.groups.GroupsOnClass2"/>
<class name="com.course.testng.groups.GroupsOnClass3"/>
</classes>
</test>
</suite>
說明:
測試套件suitename中有2個(gè)測試模塊,runAll和OnlyRunStu。
runAll模塊下的測試類是 GroupsOnClass1.java、GroupsOnClass2.java、GroupsOnClass3.java,執(zhí)行該測試類下所有方法。
OnlyRunStu模塊下的測試類是 GroupsOnClass1.java、GroupsOnClass2.java、GroupsOnClass3.java,只執(zhí)行分組為 stu 的類下的方法。
3、運(yùn)行xml文件,結(jié)果:
GroupsOnClass1 中的stu1111運(yùn)行
GroupsOnClass1 中的stu2222運(yùn)行
GroupsOnClass2 中的stu1111運(yùn)行
GroupsOnClass2 中的stu2222運(yùn)行
GroupsOnClass3 中的teacher1111運(yùn)行
GroupsOnClass3 中的teacher2222運(yùn)行
GroupsOnClass1 中的stu1111運(yùn)行
GroupsOnClass1 中的stu2222運(yùn)行
GroupsOnClass2 中的stu1111運(yùn)行
GroupsOnClass2 中的stu2222運(yùn)行
六、異常測試
1、什么時(shí)候會用到異常測試
在我們期望結(jié)果為某個(gè)異常時(shí),比如:我們傳入了某些不合法參數(shù),程序拋出了異常。也就是說預(yù)期結(jié)果就是該異常。
2、運(yùn)行時(shí)異常和非運(yùn)行時(shí)異常

3、舉例
1)新建 ExpectedException.java
package com.course.testng;
import org.testng.annotations.Test;
public class ExpectedException {
// 這是一個(gè)結(jié)果會失敗的異常
@Test(expectedExceptions = RuntimeException.class)
public void runTimeExceptionFailed(){
System.out.println("這是一個(gè)失敗的異常測試");
}
// 這是一個(gè)成功的異常測試
@Test(expectedExceptions = RuntimeException.class)
public void runTimeExceptionSuccess(){
System.out.println("這是我的異常測試!");
throw new RuntimeException();
}
}
2)結(jié)果:兩個(gè)用例,pass一個(gè),fail一個(gè)

七、依賴測試
7.1 依賴測試
有時(shí)我們可能需要以特定順序調(diào)用測試用例中的方法,或者希望在方法之間共享一些數(shù)據(jù)和狀態(tài)。
TestNG支持這種依賴關(guān)系,因?yàn)樗С衷跍y試方法之間顯式依賴的聲明。
TestNG允許指定依賴關(guān)系:
- 在
@Test注解中使用屬性dependsOnMethods - 在
@Test注解中使用屬性dependsOnGroups
在TestNG中,我們使用dependOnMethods和dependsOnGroups來實(shí)現(xiàn)依賴測試。 如果依賴方法失敗,則將跳過所有后續(xù)測試方法。
舉例:test1() 依賴于 test2()
7.2 dependsOnMethods
1、如果test1()成功,則執(zhí)行test2()
新建 DependTest.java
package com.course.testng;
import org.testng.annotations.Test;
public class DependTest {
@Test
public void test1(){
System.out.println("test1 run");
}
@Test(dependsOnMethods = {"test1"})
public void test2(){
System.out.println("test2 run");
}
}
只運(yùn)行 test2() ,結(jié)果:

2、如果test1()失敗,則跳過test2()
修改test1(),使得其運(yùn)行失敗;test2 依賴的方法失敗,自己也不會運(yùn)行成功
package com.course.testng;
import org.testng.annotations.Test;
public class DependTest {
@Test
public void test1(){
System.out.println("test1 run");
throw new RuntimeException();
}
@Test(dependsOnMethods = {"test1"})
public void test2(){
System.out.println("test2 run");
}
}
只運(yùn)行 test2() ,結(jié)果:用例執(zhí)行失敗一個(gè),忽略一個(gè)

7.3 dependsOnGroups
1、創(chuàng)建java文件:TestServer.java
package com.course.testng.depend;
import org.testng.annotations.Test;
@Test(groups = "deploy")
public class TestServer {
@Test
public void deployServer(){
System.out.println("運(yùn)行服務(wù)");
}
@Test(dependsOnMethods = "deployServer")
public void deployBackUpServer(){
System.out.println("如果方法deployServer()成功則運(yùn)行");
}
}
2、創(chuàng)建 TestDatabase.java
package com.course.testng.depend;
import org.testng.annotations.Test;
public class TestDatabase {
@Test(groups = "db",dependsOnGroups = "deploy")
public void initDB(){
System.out.println("這是initDB方法");
}
@Test(dependsOnMethods = {"initDB"},groups = "db")
public void testConnection(){
System.out.println("這是testConnection方法");
}
}
3、創(chuàng)建TestApp.java
package com.course.testng.depend;
import org.testng.annotations.Test;
public class TestApp {
@Test(dependsOnGroups = {"deploy","db"})
public void method1(){
System.out.println("這是方法1");
}
@Test(dependsOnMethods = "method1")
public void method2(){
System.out.println("這是方法2");
}
}
4、在resource文件下創(chuàng)建depend.xml
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="TestDependency">
<test name="TestCase1">
<classes>
<class name="com.course.testng.depend.TestApp">
</class>
<class name="com.course.testng.depend.TestDatabase">
</class>
<class name="com.course.testng.depend.TestServer">
</class>
</classes>
</test>
</suite>
5、執(zhí)行結(jié)果:

八、參數(shù)化測試
8.1 參數(shù)化測試-xml文件參數(shù)化
1、如何通過外部或內(nèi)部傳遞參數(shù)
2、創(chuàng)建包 parameter,創(chuàng)建類 ParameterTest.java
package com.course.testng.parameter;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
public class ParameterTest {
@Test
@Parameters({"name","age"})
public void paramTest1(String name,String age){
System.out.println("name = "+name+"; age = "+age);
}
}
3、創(chuàng)建Parameter.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="All Test Suite">
<test verbose="2" preserve-order="true" name="D:/testngDemo">
<parameter name="name" value="tom"/>
<parameter name="age" value="18"/>
<classes>
<class name="com.course.testng.parameter.ParameterTest"/>
</classes>
</test>
</suite>
4、運(yùn)行xml文件,運(yùn)行結(jié)果

8.2 參數(shù)化測試-DataProvider
1、第一種參數(shù)化方式其實(shí)比較雞肋,第二種方式才是TestNG參數(shù)化的靈魂,用到了@DataProvider,它會返回一個(gè)二維數(shù)組:
package org.example;
import org.testng.Assert;
import org.testng.annotations.*;
public class AppTest {
// 定義一個(gè)數(shù)據(jù)提供器,叫test01,返回二維數(shù)組
@DataProvider(name = "test01")
public Object[][] data() {
return new Object[][] {
{"tom", 18},
{"jack", 20}
};
}
// 引用這個(gè)數(shù)據(jù)提供器
@Test(dataProvider = "test01")
public void test01(String name, int age){
System.out.println(name + ": " + age);
}
}
結(jié)果:

切記:
- @DataProvider用于生產(chǎn)數(shù)據(jù),name是唯一標(biāo)識。
- 在@Test中通過dataProvider屬性指定name。
- 測試方法的入?yún)⒏鷶?shù)組中元素一一對應(yīng)。
2、Iterator
@DataProvide的返回值(參數(shù)類型)除了已經(jīng)提到的Object[][],還可以時(shí)Iterator,它不會一次性生成所有數(shù)據(jù),而是每調(diào)用一次生成一次,節(jié)約內(nèi)存。
package com.course.testng.parameter;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.Iterator;
public class ParameterTest3 {
@DataProvider(name = "test01")
public Iterator<Object[]> data(){
Object[][] myObject = new Object[][]{
{"tom",18},
{"jack",20}
};
return Arrays.asList(myObject).iterator();
}
@Test(dataProvider = "test01")
public void test01(String name,int age){
System.out.println(name+":"+age);
}
}
結(jié)果:

總結(jié):DataProvider支持?jǐn)?shù)組、迭代器
3、通過方法名傳遞參數(shù)
package com.course.testng.parameter;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.lang.reflect.Method;
public class ParameterTest4 {
@Test(dataProvider = "methodData")
public void test1(String name,int age){
System.out.println("test1方法 name="+name+";age="+age);
}
@Test(dataProvider = "methodData")
public void test2(String name,int age){
System.out.println("test2方法 name="+name+";age="+age);
}
@DataProvider(name = "methodData")
public Object[][] methodDataTest(Method method){
Object[][] result = null;
if (method.getName().equals("test1")){
result = new Object[][]{
{"hqq1",18},
{"hqq2",19}
};
}else if (method.getName().equals("test2")){
result = new Object[][]{
{"guihua1",20},
{"guihua2",22}
};
}
return result;
}
}
結(jié)果:
test1方法 name=hqq1;age=18
test1方法 name=hqq2;age=19
test2方法 name=guihua1;age=20
test2方法 name=guihua2;age=22
九、多線程測試
實(shí)現(xiàn)testng多線程的兩種方式:
- 注解實(shí)現(xiàn)
- xml實(shí)現(xiàn)
9.1 注解實(shí)現(xiàn)
package com.course.testng.thread;
import org.testng.annotations.Test;
public class MultiThreadByAnnotation {
/**
* threadPoolSize 為線程池內(nèi)可使用的線程數(shù)
* 使用threadPoolSize個(gè)線程,將test方法執(zhí)行invocationCount次
* timeOut配置的是每次執(zhí)行該測試方法所耗費(fèi)時(shí)間的閾值,超過閾值則測試失敗
*/
@Test(invocationCount = 10,threadPoolSize = 3,timeOut = 1000)
public void test(){
System.out.println("hello");
System.out.println("Thread Id:"+Thread.currentThread().getId());
}
}
執(zhí)行結(jié)果:使用了3個(gè)線程,將測試方法test執(zhí)行了10次。
hello
hello
hello
Thread Id:12
Thread Id:13
Thread Id:14
hello
hello
Thread Id:12
hello
Thread Id:14
Thread Id:13
hello
Thread Id:12
Thread Id:14
hello
hello
Thread Id:13
hello
Thread Id:12
9.2 xml實(shí)現(xiàn)test、class、method級別的并發(fā)
1)需要在 testng.xml中suite tag下設(shè)置
<suite name="Testng Parallel Test" parallel="tests" thread-count="5">
<suite name="Testng Parallel Test" parallel="classes" thread-count="5">
<suite name="Testng Parallel Test" parallel="methods" thread-count="5">
他們的共同點(diǎn)都是最多起5個(gè)線程去同時(shí)執(zhí)行不同的用例。thread-count 代表最大并發(fā)線程數(shù)。
他們的區(qū)別如下:
- method級別:所有用例都可以在不同的線程去執(zhí)行
- class級別:不同class tag 下的用例,在不同的線程執(zhí)行;相同class tag下的用例只能在同一個(gè)線程中執(zhí)行
- tests級別:不同test tag 下的用例,在不同的線程執(zhí)行;相同test tag下的用例只能在同一個(gè)線程中執(zhí)行
意義:可以將非線程安全的測試類或group統(tǒng)一放到一個(gè)test中,這樣在并發(fā)的同時(shí)又可以保證這些類里面的用例是單線程執(zhí)行。也可以根據(jù)需要設(shè)定class級別的并發(fā),讓同一個(gè)測試類里的用例在同一個(gè)線程中執(zhí)行。
補(bǔ)充:xml文件配置這種方式不能指定線程池,只有方法上才可以指定線程池。
2)舉例實(shí)現(xiàn)1:methods
package com.course.testng.thread;
import org.testng.annotations.Test;
public class MultiThreadByXml {
@Test
public void test1(){
System.out.println("test1---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test2(){
System.out.println("test2---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test3(){
System.out.println("test3---Thread Id : " + Thread.currentThread().getId());
}
}
新增multiThread.xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!--parallel="methods",表示多線程級別為方法級別-->
<!--thread-count="2",表示線程數(shù)為2-->
<suite name="thread" parallel="methods" thread-count="2">
<test name="demo1">
<classes>
<class name="com.course.testng.thread.MultiThreadByXml"/>
</classes>
</test>
</suite>
執(zhí)行xml文件:

說明:所有用例都可以在不同的線程去執(zhí)行。
3)舉例實(shí)現(xiàn)2:classes
新增 MultiThreadByXml.java、MultiThreadByXml2.java、MultiThreadByXml3.java
package com.course.testng.thread;
import org.testng.annotations.Test;
public class MultiThreadByXml {
@Test
public void test1(){
System.out.println("test1---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test2(){
System.out.println("test2---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test3(){
System.out.println("test3---Thread Id : " + Thread.currentThread().getId());
}
}
package com.course.testng.thread;
import org.testng.annotations.Test;
public class MultiThreadByXml2 {
@Test
public void test1(){
System.out.println("test21---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test2(){
System.out.println("test22---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test3(){
System.out.println("test23---Thread Id : " + Thread.currentThread().getId());
}
}
package com.course.testng.thread;
import org.testng.annotations.Test;
public class MultiThreadByXml3 {
@Test
public void test1(){
System.out.println("test31---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test2(){
System.out.println("test32---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test3(){
System.out.println("test33---Thread Id : " + Thread.currentThread().getId());
}
}
說明:改xml的配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!--parallel="classes",表示多線程級別為class-->
<!--thread-count="2",表示線程數(shù)為2-->
<suite name="thread" parallel="classes" thread-count="2">
<test name="demo1">
<classes>
<class name="com.course.testng.thread.MultiThreadByXml"></class>
<class name="com.course.testng.thread.MultiThreadByXml2"></class>
</classes>
</test>
<test name="demo2">
<classes>
<class name="com.course.testng.thread.MultiThreadByXml3"></class>
</classes>
</test>
</suite>
結(jié)果:

說明:不同class tag下的用例可以在不同線程中執(zhí)行;同一個(gè)class tag標(biāo)簽下的用例只能在同一個(gè)線程中執(zhí)行。
4)舉例實(shí)現(xiàn)3:tests
代碼同上一個(gè)例子,只改xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!--parallel="tests",表示多線程級別為test級別-->
<!--thread-count="2",表示線程數(shù)為2-->
<suite name="thread" parallel="tests" thread-count="2">
<test name="demo1">
<classes>
<class name="com.course.testng.thread.MultiThreadByXml"></class>
</classes>
</test>
<test name="demo2">
<classes>
<class name="com.course.testng.thread.MultiThreadByXml2"></class>
</classes>
</test>
</suite>

說明:不同test tag下的用例可以在不同的線程執(zhí)行,相同test tag下的用例只能在同一個(gè)線程中執(zhí)行。
9.3 多線程總結(jié)
參考文檔:https://testerhome.com/topics/7895
9.3.1 多線程并發(fā)執(zhí)行
必須要指出的是,通過多線程執(zhí)行用例時(shí)雖然可以大大提升用例的執(zhí)行效率,但是我們在設(shè)計(jì)用例時(shí)也要考慮到這些用例是否適合并發(fā)執(zhí)行,以及要注意多線程方式的通病:線程安全與共享變量的問題。建議是在測試代碼中,盡可能地避免使用共享變量。如果真的用到了,要慎用 synchronized 關(guān)鍵字來對共享變量進(jìn)行加鎖同步。否則,難免你的用例執(zhí)行時(shí)可能會出現(xiàn)不穩(wěn)定的情景(經(jīng)常聽到有人提到用例執(zhí)行地不穩(wěn)定,有時(shí) 100% 通過,有時(shí)只有 90% 通過,猜測可能有一部分原因也是這個(gè)導(dǎo)致的)。
9.3.2 不同級別的并發(fā)
通常,在 TestNG 的執(zhí)行中,測試的級別由上至下可以分為suite -> test -> class -> method,箭頭的左邊元素跟右邊元素的關(guān)系是一對多的包含關(guān)系。
這里的 test 指的是 testng.xml 中的 test tag,而不是測試類里的一個(gè)@Test。測試類里的一個(gè)@Test實(shí)際上對應(yīng)這里的 method。所以我們在使用@BeforeSuite、@BeforeTest、@BeforeClass、@BeforeMethod這些標(biāo)簽的時(shí)候,它們的實(shí)際執(zhí)行順序也是按照這個(gè)級別來的。
1)suite
一般情況下,一個(gè) testng.xml 只包含一個(gè) suite。如果想起多個(gè)線程執(zhí)行不同的 suite,官方給出的方法是:通過命令行的方式來指定線程池的容量。
java org.testng.TestNG -suitethreadpoolsize 3 testng1.xml testng2.xml testng3.xml
即可通過三個(gè)線程來分別執(zhí)行 testng1.xml、testng2.xml、testng3.xml。
實(shí)際上這種情況在實(shí)際中應(yīng)用地并不多見,我們的測試用例往往放在一個(gè) suite 中,如果真需要執(zhí)行不同的 suite,往往也是在不同的環(huán)境中去執(zhí)行,屆時(shí)也自然而然會做一些其他的配置(如環(huán)境變量)更改,會有不同的進(jìn)程去執(zhí)行。因此這種方式不多贅述。
2)test, class, method
test,class,method 級別的并發(fā),可以通過在 testng.xml 中的 suite tag 下設(shè)置。
- tests 級別:不同 test tag 下的用例可以在不同的線程執(zhí)行,相同 test tag 下的用例只能在同一個(gè)線程中執(zhí)行。
- classs 級別:不同 class tag 下的用例可以在不同的線程執(zhí)行,相同 class tag 下的用例只能在同一個(gè)線程中執(zhí)行。
- methods 級別:所有用例都可以在不同的線程去執(zhí)行。
十、超時(shí)測試
10.1 套件級別的超時(shí)測試示例
public class TimeoutSuite
{
@Test
public void timeTestOne() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Time test method one");
}
@Test
public void timeTestTwo() throws InterruptedException {
Thread.sleep(400);
System.out.println("Time test method two");
}
}
添加對應(yīng)的testng.xml文件
<suite name="Time test Suite" time-out="500" verbose="1" >
<test name="Timeout Test" >
<classes>
<class name="com.howtodoinjava.test.TimeoutSuite"/>
</classes>
</test>
</suite>
運(yùn)行結(jié)果:
[TestNG] Running: C:\somepath\TestNGExamples\testng.xml
Time test method two
===============================================
Time test Suite
Total tests run: 2, Failures: 1, Skips: 0
===============================================
從測試結(jié)果中可以看出,只有timeTestTwo()被執(zhí)行,因?yàn)樗膱?zhí)行時(shí)間少于testng.xml文件中定義的超時(shí)時(shí)間。
timeTestOne()執(zhí)行被取消,因?yàn)閳?zhí)行完成所需的時(shí)間,超過配置的超時(shí)時(shí)間。
10.2 方法級別的超時(shí)測試
public class TimeoutSuite
{
@Test(timeOut=500)
public void timeTestOne() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Time test method one");
}
@Test(timeOut=500)
public void timeTestTwo() throws InterruptedException {
Thread.sleep(400);
System.out.println("Time test method two");
}
}
執(zhí)行結(jié)果:
[[TestNG] Running: C:\Users\somepath\testng-customsuite.xml
Time test method two
PASSED: timeTestTwo
FAILED: timeTestOne
org.testng.internal.thread.ThreadTimeoutException: Method org.testng.internal.TestNGMethod.timeTestOne() didn't finish within the time-out 500
===============================================
Default test
Tests run: 2, Failures: 1, Skips: 0
===============================================
說明:@Test(timeOut=xxx) 指定超時(shí)時(shí)間,單位為毫秒。
十一、軟斷言和硬斷言
TestNG提供兩種斷言方式,分別是硬斷言或或者軟斷言,類似于pytest里面的斷言和多重?cái)嘌浴?br>
硬斷言時(shí)Assert直接調(diào)用靜態(tài)方法,軟斷言需要實(shí)例化,才能調(diào)用斷言方法。
硬斷言(Assert):當(dāng)一個(gè)測試用例中存在多個(gè)斷言,當(dāng)有一個(gè)斷言失敗時(shí),則會拋出異常,不再執(zhí)行該用例中后續(xù)的斷言;
軟斷言(SoftAssert):當(dāng)一個(gè)測試用例中存在多個(gè)斷言,當(dāng)有一個(gè)斷言失敗時(shí),會執(zhí)行后續(xù)的斷言。
11.1 硬斷言
Assert.assertEquals(actual,expected) 查看兩個(gè)對象是否相等;類似于字符串比較實(shí)用equals()方法
Assert.assertNotEquals(actual,expected) 查看兩個(gè)對象是否不相等;
Assert.assertNull(object) 查看對象是否為空;
Assert.assertNotNull(object) 查看對象是否不為空;
Assert.assertSame(actual,expected) 查看兩個(gè)對象的引用是否相等,類似于使用“==”比較兩個(gè)對象
Assert.assertNotSame(actual,expected) 查看兩個(gè)對象的引用是否不相等,類似于使用“!=”比較兩個(gè)對象
Assert.assertTrue(condition) 判斷條件是否為true
Assert.assertFalse(condition) 判斷條件是否為false
assertArrayEquals(actual,expected) 判斷兩個(gè)數(shù)組是否相等。
Assert.fail(); 讓測試用例失敗
11.2 軟斷言
SoftAssert的特點(diǎn):
- 如果一個(gè)斷言失敗,會繼續(xù)執(zhí)行這個(gè)斷言下的其他語句或者斷言
- 也就是一個(gè)用例有多個(gè)斷言,失敗了其中一個(gè),不影響其他斷言的運(yùn)行
- 千萬不要忘記在該用例的最后一個(gè)斷言后面調(diào)用assertAll(),否則斷言不執(zhí)行
總結(jié)
在實(shí)際的測試工作中,一個(gè)測試用例中往往包含多個(gè)斷言,更適合用軟斷言,一個(gè)斷言失敗,不影響其他的斷言。可能會有疑問,不管是軟斷言還是硬斷言,當(dāng)用例失敗時(shí),都需要我們?nèi)藶榈娜z查錯誤,認(rèn)為兩者是沒有區(qū)別的。
其實(shí)還是有區(qū)別的,硬斷言報(bào)錯時(shí)只知道當(dāng)前斷言異常,并不知道后續(xù)斷言是否成功。只調(diào)整了當(dāng)前的斷言,之后再次運(yùn)行腳本,后續(xù)斷言還有出錯的概率,需要反復(fù)運(yùn)行確定斷言的正確性,很是耽誤測試時(shí)間,影響工作效率。而軟斷言呢?可以知道所有失敗的斷言,可以統(tǒng)一進(jìn)行調(diào)整。

浙公網(wǎng)安備 33010602011771號