TestNG單元測試與使用詳解


TestNG的基本注解與執行順序

在類里編輯程序時,在@Test后面,摁 alt+回車,選擇對應的插件,可以把目前用到的插件自動添加到 pom.xml 文件中,如下面的testng,每摁一次,就多添加一次

當使用的類里,用到的方法沒有導入類包時,可以在 類的大括號 后面,摁 alt+回車 ,可以自動導入目前的類中,需要用到的類包,如 import org.testng.annotations.Test;

1、注解實戰BeforeMethod和AfterMethod

  • @BeforeMethod,在每次測試方法運行之前運行;

  • @AfterMethod,在每次測試方法運行之后運行。

  • 比如:

  • 運行結果為:

2、注解實戰BeforeClass和AfterClass

  • @BeforeClass,每次當前類運行之前,運行一次;
  • @AfterClass,每次當前類運行結束后,運行一次。

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("BeforeMethon這是在測試方法之前運行的");
    }

    @AfterMethod
    public void afterMethod(){
        System.out.println("AfterMethod這是在測試方法之后運行的");
    }

    @BeforeClass
    public void beforeClass(){
        System.out.println("beforeClass這是在類運行之前運行的方法");
    }

    @AfterClass
    public void afterClass(){
        System.out.println("afterClass這是在類運行之后運行的方法");
    }
}
  • 運行結果為:

3、注解實戰BeforeSuite和AfterSuite

  • @BeforeSuite測試套件,在當前測試套件下所有類運行之前運行一次,包含該套件下所有類;
  • @AfterSuite測試套件,在當前測試套件下所有類運行結束之后運行一次,包含該套件下所有類。

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("BeforeMethon這是在測試方法之前運行的");
    }

    @AfterMethod
    public void afterMethod(){
        System.out.println("AfterMethod這是在測試方法之后運行的");
    }

    @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測試套件,在當前測試套件下所有類運行結束之后運行一次,包含該套件下所有類");
    }
}
  • 運行結果為:

4、套件測試

  • 1、在包下,新建名為suite的包,用來管理測試套件
  • 2、在測試套件包下,新建一個測試套件配置類,用來對所有測試套件進行配置
  • 如,SuiteConfig,里面放的是運行測試套件前,需要運行的方法,是公共方法。
  • 3、再新建測試類,如LoginTest,里面存放的是@test底下的方法,即需要運行的測試方法。
  • Resources里面,存放的是@test的配置文件XML
  • 4、新建XML配置文件

  • 5、編輯相關文件


SuiteConfig.java

package com.units.mytest.suite;

import org.testng.annotations.AfterSuite;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;

public class SuiteConfig {

    @BeforeSuite
    public void beforeSuite(){
        System.out.println("before suite運行啦");
    }
    @AfterSuite
    public void aftersuite(){
        System.out.println("after suite 運行啦");
    }

    @BeforeTest
    public void beforeTest(){
        System.out.println("運行beforeTest");
    }

    @AfterTest
    public void afterTest(){
        System.out.println("運行aftertest");
    }
}

LoginTest.java

package com.units.mytest.suite;
import org.testng.annotations.Test;

public class LoginTest {

    @Test
    public void loginTaoBao(){
        System.out.println("淘寶登陸成功");
    }
}

PayTest.java

package com.units.mytest.suite;
import org.testng.annotations.Test;

public class PayTest {
    
    @Test
    public void paySuccess(){
        System.out.println("支付寶支付成功");
    }
}
  • suite.xml,在每個類標簽里都放入SuiteConfig,說明該配置類里的方法對每個類都有效,里面的方法是公共方法。

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="test">
    <test name="login">
        <classes>
            <class name="com.units.mytest.suite.SuiteConfig"/>
            <class name="com.units.mytest.suite.LoginTest"/>
        </classes>
    </test>
    <test name="pay">
        <classes>
            <class name="com.units.mytest.suite.SuiteConfig"/>
            <class name="com.units.mytest.suite.PayTest"/>
        </classes>
    </test>
</suite>
  • 運行結果為:

5、忽略測試

  • 1、新建忽略測試類 IgnoreTest.java
  • 2、通過@Test(enabled = false)來控制,false表示忽略當前測試,不寫默認為true,執行,如下

public class IgnoreTest {

    @Test
    public void ignore1(){
        System.out.println("ignore1 執行");
    }

    @Test(enabled = false)
    public void ignore2(){
        System.out.println("ignore2 執行");
    }

    @Test(enabled = true)
    public void ignore3(){
        System.out.println("ignore3 執行");
    }
}
  • 運行結果為:

6、組測試中的方法分組測試(方法組)

  • 1、創建一個groups包
  • 2、在組包下,創建方法分組測試類 GroupsOnMethod
  • 3、在該類下的方法,都附注上組方法標簽,如下,分成了兩組server、client

public class GroupsOnMethod {

    @Test(groups = "server") //組名
    public void test1(){
        System.out.println("這是服務端組的測試方法11111");
    }

    @Test(groups = "server")
    public void test2(){
        System.out.println("這是服務端組的測試方法22222");
    }

    @Test(groups = "client")
    public void test3(){
        System.out.println("這是服務端組的測試方法33333");
    }

    @Test(groups = "client")
    public void test4(){
        System.out.println("這是服務端組的測試方法44444");
    }

    @BeforeGroups("server")
    public void beforeGroupsOnServer(){
        System.out.println("這是服務端組運行之前運行的方法!!!!!");
    }

    @AfterGroups("server")
    public void afterGroupsOnServer(){
        System.out.println("這是服務端組運行之后運行的方法!!!!!");
    }

    @BeforeGroups("client")
    public void beforeGroupsOnClient(){
        System.out.println("這是客戶端組運行之前運行的方法!!!!!");
    }

    @AfterGroups("client")
    public void afterGroupsOnClient(){
        System.out.println("這是客戶端組運行之后運行的方法!!!!!");
    }
}
  • 運行結果為:

    實現了分組測試。若不加@BeforeGroups("client")等,則組方法按順序執行,不額外執行其他方法。

7、組測試中的類分組測試(按類進行分組測試)

  • 1、新建三個類

  • 2、類的內容如下:


GroupsOnClass1.java

@Test(groups = "stu")
public class GroupsOnClass1 {
    public void stu1(){
        System.out.println("GroupsOnClass1中的stu1運行");
    }

    public void stu2(){
        System.out.println("GroupsOnClass1中的stu2運行");
    }
}

GroupsOnClass2.java

Test(groups = "stu")
public class GroupsOnClass2 {
    public void stu1(){
        System.out.println("GroupsOnClass222中的stu1運行");
    }

    public void stu2(){
        System.out.println("GroupsOnClass222中的stu2運行");
    }
}

GroupsOnClass3.java

@Test(groups = "teacher")
public class GroupsOnClass3 {
    public void teacher1(){
        System.out.println("GroupsOnClass3中的teacher1運行");
    }

    public void teacher2(){
        System.out.println("GroupsOnClass3中的teacher2運行");
    }
}

其中,前面兩個類屬於stu組,第三個類屬於teacher組

  • 3、新建一個執行文件GroupsOnClass.xml,內容如下

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="suitename">
    <test name="runAll">    <!—執行所有的測試類-->
        <classes>
            <class name="com.units.mytest.groups.GroupsOnClass1"/>
            <class name="com.units.mytest.groups.GroupsOnClass2"/>
            <class name="com.units.mytest.groups.GroupsOnClass3"/>
        </classes>
    </test>
    <test name="onlyRunStu">
        <groups>
            <run>
                <include name="stu"/>   <!—僅執行組類名為stu的測試類-->
            </run>
        </groups>
        <classes>
            <class name="com.units.mytest.groups.GroupsOnClass1"/>
            <class name="com.units.mytest.groups.GroupsOnClass2"/>
            <class name="com.units.mytest.groups.GroupsOnClass3"/>
        </classes>
    </test>
</suite>
  • 運行結果為:

8、異常測試

  • 1、新建一個異常測試類ExpectedException.java

public class ExpectedException {

    /*什么時候會用到異常測試?
    * 在我們期望結果為某一個異常的時候
    * 比如:我們傳入了某些不合法的參數,程序拋出了異常
    * 也就是說我的語句結果就是這個異常。
    * */

    // 這是一個測試結果會失敗的異常測試
    @Test(expectedExceptions = RuntimeException.class)    //期望結果為運行時異常
    public void runTimeExceptionFailed(){
        System.out.println("這是一個失敗的異常測試");
    }

    // 這是一個成功的異常測試
    @Test(expectedExceptions = RuntimeException.class)
    public void runTimeExceptionSuccess(){
        System.out.println("這是一個成功的異常測試");
        throw new RuntimeException();   //強制拋出一個運行時異常
    }
}
  • 運行結果為:

    測試結果1失敗,測試結果2成功。測試結果1沒有異常產生,因而失敗;測試結果2有期望異常產生,因而成功。

9、依賴測試

  • hard依賴:默認為此依賴方式,即其所有依賴的methods或者groups必須全部pass,否則被標識依賴的類或者方法將會被略過,在報告中標識為skip,如后面的范例所示,此為默認的依賴方式;
  • soft依賴:此方式下,其依賴的方法或者組有不是全部pass也不會影響被標識依賴的類或者方法的運行,注意如果使用此方式,則依賴者和被依賴者之間必須不存在成功失敗的因果關系,否則會導致用例失敗。此方法在注解中需要加入alwaysRun=true即可.

9.1、使用注解的依賴

  • 第一種,依賴方法 dependsOnMethods

package unittests;

import java.lang.reflect.Method;
import org.testng.annotations.*;

public class Test11 {

	@DataProvider(name = "dp")   //提供參數化數據
	public Object[][] createData(Method m){
	// 打印測試方法名
		System.out.println(m.getName());
		return new Object[][]{new Object[]{"qctest"}};
	}
	
	@Test(dataProvider = "dp", dependsOnMethods = { "test2" })
	public void test1(String s){
	}
	
	@Test(dataProvider = "dp")
	public void test2(String s){
		//test2運行失敗情況
		int a = 1/0;
	}
	
}

上面程序,test1 的運行依賴 test2 的運行。此時會先運行test2,然后再運行test1。
注意:這種依賴,如果被依賴者運行失敗,那么依賴者將不被執行,報告中被標記為 SKIP。結果如下


test2
FAILED: test2("qctest")
java.lang.ArithmeticException: / by zero
…………
SKIPPED: test1
java.lang.Throwable: Method Test11.test1(java.lang.String)[pri:0, instance:unittests.Test11@9629756] depends on not successfully finished methods
…………
===============================================
    Default test
    Tests run: 2, Failures: 1, Skips: 1
===============================================
  • 利用@Test 的屬性設置 alwaysRun=true 解決上例中被依賴者運行失敗,不執行依賴者的問題,如下:

package unittests;

import java.lang.reflect.Method;
import org.testng.annotations.*;

public class Test12 {

	@DataProvider(name = "dp")
	public Object[][] createData(Method m){
		// 打印測試方法名
		System.out.println(m.getName());
		return new Object[][]{new Object[]{"qctest"}};
	}
	
	//alwaysRun=true時,若被依賴者運行失敗,則依賴者會繼續運行
	@Test(dataProvider = "dp", dependsOnMethods = { "test2" },
	alwaysRun=true)
	public void test1(String s){
	}
	
	@Test(dataProvider = "dp")
	public void test2(String s){
		int a = 1/0;
	}
	
}

運行結果如下,不管被依賴者運行失敗與否,依賴者都會被執行:


[RemoteTestNG] detected TestNG version 6.14.3
test2
test1
PASSED: test1("qctest")
FAILED: test2("qctest")
  • 第二種,依賴組 dependsOnGroups (dependsOnGroups 和 dependsOnMethods 都可接收正則表達式)

package unittests;

import java.lang.reflect.Method;
import org.testng.annotations.*;

public class Test13 {

	@DataProvider(name = "dp")
	public Object[][] createData(Method m){
	// 打印測試方法名
		System.out.println(m.getName());
		return new Object[][]{new Object[]{"qctest"}};
	}
	
	@Test(dataProvider = "dp", dependsOnGroups = { "init" }, alwaysRun=true)
	public void test1(String s){
	}
	
	@Test(dataProvider = "dp", groups = "init")
	public void test2(String s){
		int a = 1/0;
	}
	
}

執行結果如下,init組里的所有方法先執行,然后再執行test1方法:


[RemoteTestNG] detected TestNG version 6.14.3
test2
test1
PASSED: test1("qctest")
FAILED: test2("qctest")

默認的,依賴方法按組分類,如果方法 test1 和方法 test2 都是同一個測試類的測試方法,
test1依賴test2,那么當該類的多個實例被調用時,會先執行完所有test2,再執行test1。(即該類被多次實例化調用時,都是先執行完所有實例化類的test2,然后才執行test1),如下:


Test14.java

package unittests;

import org.testng.annotations.DataProvider;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;
import unittests.Test14_2;

public class Test14 {

	@Factory(dataProvider = "dp")
	public Object[] f(int n) {
		//結果為一維數組
		Object[] result = new Object[n];
		for(int i=0; i<n; i++){
			//每遍歷一次,即實例化一次Test14_2,並把結果存儲在result的數組中
			result[i] = new Test14_2();
		}
		//返回result整個數組
		return result;
	}
	
	//參數化的數據保存在該方法中
	@DataProvider
	public Object[][] dp() {
		return new Object[][]{
		new Object[] { 5 }
		};
	}
	
}

Test14_2.java

package unittests;

import org.testng.annotations.Test;

public class Test14_2 {

	@Test(dependsOnMethods = "test2")
	public void test1() {
		System.out.println("test1");
	}
	
	@Test
	public void test2(){
		System.out.println("test2");
	}
	
}

xmltest14.xml

<!-- 從同一個 XML 文件運行的並行 data provider 共享相同的線程池,線程池大小默認為 10.
可通過修改 XML 文件中的<suite>標簽來修改。
<suite name="Suite1" data-provider-thread-count="20" >
...
如果想在不同的線程池中運行多個 data provider,需要從 xml 文件中運行它們。 -->

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="suite" verbose="1">
	<test name="MyTest1">
		<classes>
			<class name="unittests.Test14" />
		</classes>
	</test>
</suite>

以testng的方式運行xmltest14.xml,執行類包Test14,結果如下,先執行完所有test2,才執行test1:


[RemoteTestNG] detected TestNG version 6.14.3
test2
test2
test2
test2
test2
test1
test1
test1
test1
test1

===============================================
suite
Total tests run: 10, Failures: 0, Skips: 0
===============================================
  • 默認的,依賴方法按組分類,如果方法 test1 和方法 test2 都是同一個測試類的測試方法,
    test1依賴test2,那么當該類的多個實例被調用時,會先執行完所有test2,再執行test1。但若需要在該類的多個實例被調用時,都是執行一次test2,然后執行一次test1,接着再test2這樣輪回,則解決方法如下:

解決方法:xml 文件中增加 group-by-instances="true"    
比如:
<suite name="Factory" group-by-instances="true">
或者
<test name="Factory" group-by-instances="true">

修改后的具體內容如下 xmltest14_2.xml


<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="suite" verbose="1" group-by-instances="true">
	<test name="MyTest1">
		<classes>
			<class name="unittests.Test14" />
		</classes>
	</test>
</suite>

以testng的方式運行xmltest14_2.xml,執行類包Test14,結果如下,test2和test1輪回輸出:


[RemoteTestNG] detected TestNG version 6.14.3
test2
test1
test2
test1
test2
test1
test2
test1
test2
test1

===============================================
suite
Total tests run: 10, Failures: 0, Skips: 0
===============================================

注:TestNG中@Factory與@DataProvider的功能大相徑庭,但是在多次執行的問題上常常理不清。

1.@DataProvider

首先,必須明確,使用@DataProvider修飾的方法,只負責提供測試數據,而且是測試執行前就確定了的靜態數據。該方法必須被其他測試方法引用才意義。

其次,必須強調,使用@DataProvider修飾的方法,其返回的數據類型必須為Object[][]。為什么必須是二維數組呢?其中的一維表示單位測試數據,而另一維表示可以提供多組獨立的測試數據,供逐一使用。

@DataProvider
public Object[][] dataMethod() {
    return new Object[][] { { "one" }, { "two" } };
}

所以,當@DataProvider修飾的方法提供了多組測試數據時,引用@DataProvider的測試方法,就會被多次執行,每次執行使用@DataProvider的一組測試數據。即@DataProvider修飾的方法中有多少組測試數據,引用@DataProvider的測試方法就會被執行多少次。但是自始至終只有一個測試類的實例,即該實例的一個測試方法被多次執行。

@Test(dataProvider = "dataMethod")
public void testMethod(String param) {
    System.out.println("The parameter value is: " + param);
}

2. @Factory

首先,使用@Factory修飾的方法,負責提供測試類的實例。只是測試類的構造函數帶了參數,為了構造不同的測試類實例,每次都要給測試類的構造函數傳遞不同的測試參數,從而構造不同的測試類的實例。@Factory修飾的方法永遠返回Object[]類型。

其次,測試類中無需顯式引用@Factory修飾的方法,因為該方法就提供測試類的實例,不需要調用。執行測試時,先構造一個測試類的實例,再執行該測試類中的所有測試方法。然后再構造一個新的測試類的實例,並執行其所有測試方法,直到所有的測試類的實例都被構造執行。即@Factory修飾的方法中有多少個測試類的實例,就會執行多少遍該測試類中的所有測試方法。

@Factory
public Object[] factoryMethod() {
    return new Object[] {
        new SimpleTest("one"),
        new SimpleTest("two")
    };
}

3. 在@Factory方法中引用@DataProvider方法

既然@Factory修飾的方法,也需要測試數據用以構造測試類的實例,那么該方法還可以引用@DataProvider修飾的方法,以構造不同的測試類的實例。

@Factory(dataProvider = "dataMethod")
public Object[] factoryMethod(String param) {
    return new Object[] {
        new SimpleTest(param)
    };
}

4. 不同測試類的實例

此外,@Factory修飾的方法不僅僅能夠構造一個測試類的不同實例,還能夠構造不同測試類的實例。

@Factory(dataProvider = "dataMethod")
public Object[] factoryMethod(String param) {
    return new Object[] {
        new SimpleTest(param), 
        new ComplicateTest(param)
    };
}

使用上述@dataProvider作為參數數據,將分別構造兩個SimpleTest測試類實例,和ComplicateTest兩個測試類實例。

9.3、在XML中實現依賴,使用<dependencies>標簽


Test15.java

package unittests;

import java.lang.reflect.Method;
import org.testng.annotations.*;

public class Test15 {

	@Test(groups = "other")
	public void test3(){
		System.out.print("I am qctest");
	}
	
	@DataProvider(name = "dp")
	public Object[][] createData(Method m){
	// 打印測試方法名
		System.out.println(m.getName());
		return new Object[][]{new Object[]{"qctest"}};
	}
	
	@Test(dataProvider = "dp", groups = "init1")
	public void test1(String s){
	}
	
	@Test(dataProvider = "dp", groups = "init2")
	public void test2(String s){
	}
	
}

xmltest15.xml

<!-- 可選的,可在 testng.xml 文件中指定組依賴。使用<dependencies>標簽來實現這個。
注意:被依賴的多個組之間用空格分隔。 -->

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="suite" verbose="1">
	<test name="MyTest1">
		<groups>
			<dependencies>
				<group name ="others" depends-on="init1 init2" />
			</dependencies>
		</groups>
		<classes>
			<class name="unittests.Test15" />
		</classes>
	</test>
</suite>

以testng的方式運行xmltest15.xml,運行類test15,結果如下:


[RemoteTestNG] detected TestNG version 6.14.3
test1
test2
I am qctest
===============================================
suite
Total tests run: 3, Failures: 0, Skips: 0
===============================================

others組的方法,依賴於init1和init2組的方法的運行

10、參數化測試-xml文件參數化

  • 新建一個包,在包下新建需參數化的測試類 ParamterTest.java

public class ParamterTest {

    @Test
    @Parameters({"name","age"})     //參數變量名
    public void paramTest1(String name, int age){
        System.out.println("\n name = " + name + "; age = " + age);
    }
}
  • 新建參數化XML文件 Paramter.xml

<?xml version="1.0" encoding="UTF-8" ?>
<suite name="parameter">
    <test name="param">
        <classes>
            <parameter name="name" value="zhangsan"/>
            <parameter name="age" value="18"/>
            
            <class name="com.units.mytest.paramter.ParamterTest"/>
        </classes>
    </test>
</suite>

在parameter中定義引用的參數名和相對應的參數值。

  • 以testng方式運行Paramter.xml,結果為:

11、參數化測試-DataProvider參數化

  • 1、普通參數化

DataProviderTest.java

public class DataProviderTest {

    //通過dataProvider找到參數數據並傳入
    @Test(dataProvider = "data")
    public void testDataProvider(String name, int age){
        System.out.println("name = " + name + "; age = " + age);
    }

    //提供參數化數據
    @DataProvider(name = "data")
    public Object[][] providerData(){
        Object[][] obj = new Object[][]{
                {"zhangsan", 10},
                {"lisi", 18},
                {"wangwu", 28}
        };
        return obj;
    }
}
  • 運行DataProviderTest.java的結果為

  • 2、根據不同的方法進行不同的參數化


DataProviderTest.java

package com.units.mytest.paramter;

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.lang.reflect.Method;

public class DataProviderTest {

    @Test(dataProvider = "methodData")    //從methodData這個數據來源中獲取數據
    public void test1(String name,int age){
        System.out.println("test111方法 name=" + name + ";age=" + age);
    }

    @Test(dataProvider = "methodData")
    public void test2(String name,int age){
        System.out.println("test222方法 name=" + name + ";age=" + age);
    }

    @DataProvider(name = "methodData")    //定義一個方法數據
    public Object[][] methodDataTest(Method method){
        Object[][] result=null;     //申明一個對象
        //獲取的方法是test1時,取以下數據
        if (method.getName().equals("test1")){
            result = new Object[][]{
                    {"zhangsan",20},
                    {"lisi",25}
            };
        }
//獲取到的方法是test2時,取以下數據
        else if (method.getName().equals("test2")){
            result = new Object[][]{
                    {"wangwu", 50},
                    {"zhaoliu", 60}
            };
        }
        return result;
    }
}

12、工廠模式(動態的創建測試)

使用工廠模式管理,即構造一個新的測試類的實例,並執行其所有測試方法,直到所有的測試類的實例都被構造執行。

比如,創建了一個Object數組,長度為5,這5個元素的值都是null,然后把創建好的數組實例的引用賦給obj變量。如果需要為這些元素分配具體的對象,則需要分別指定或用{}符號進行初始化,如下所示:

Object[] obj=new Object[]{new Object(),null,null,null,new String("123")}; //引用類型的數組

或分別指定

obj[0]=new Object();//數組元素在賦對象引用

obj[4]=new String("123");

factory 方法接收類似@Test 和@Before/After 的參數,且必須返回 Object[]。返回的
對象可以是任何類實例(注:對象所在類,不含@xxxx 注解的方法不被執行)。

  • 1、新建一個待測試的類Test16_WebTest.java

package unittests;

import org.testng.annotations.Test;

public class Test16_WebTest {

	private int m_numberOfTimes;
	//構造方法
	public Test16_WebTest(int numberOfTimes){
		m_numberOfTimes = numberOfTimes;
	}
	
	@Test
	public void testServer(){
		for(int i=0; i < m_numberOfTimes; i++){
			System.out.println("access the web page" + i);
		}
	}
	
}
  • 2、新建一個工廠管理類,管理前面的測試類,實例化並傳入實參

package unittests;

import org.testng.annotations.Factory;
import unittests.Test16_WebTest;

public class Test16_WebTestFactory {

	//說明:  附注@Factory 注解的方法中構造某測試類的實例,會自動調用該測試類中帶有@Test注解的方法
	@Factory
	public Object[] createInstances(){
		//創建了一個數組實例,長度為10,每個數組元素的值均為null,並沒有創建10個Object對象,若需要創建10個Object對象,則需要為每個數組元素分別指定或用符號{}
		Object[] result = new Object[10];
		for(int i=0; i < 10; i++){
			//數組元素在賦對象引用,實例化數組對象,並執行該實例的所有方法
			result[i] = new Test16_WebTest(i * 10);
		}
		return result;
	}
	
}
  • 3、新建xmltest16.xml,用於執行前面的工廠管理類(沒有@test注解的類,一般放在xml中執行)

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="suite" verbose="1">
	<test name="MyTest1">
		<classes>
			<class name="unittests.Test16_WebTestFactory" />
		</classes>
	</test>
</suite>
  • 4、以testng的方式運行xmltest16.xml,執行結果如下

[RemoteTestNG] detected TestNG version 6.14.3
access the web page0
access the web page1
access the web page2
access the web page3
access the web page4
access the web page5
access the web page6
access the web page7
access the web page8
access the web page9
access the web page0
access the web page1
…………
access the web page16
access the web page17
access the web page18
access the web page19

===============================================
suite
Total tests run: 10, Failures: 0, Skips: 0
===============================================
  • 5、也可以Java程序中,通過代碼來運行工廠管理類Test16_WebTestFactory.java

Test16_run.java
//可以在 java 程序中通過代碼來運行TestNG

package unittests;

import org.testng.TestNG;
import unittests.Test16_WebTestFactory;

public class Test16_run {

	public static void main(String[] args){
		TestNG testNG = new TestNG();
		testNG.setTestClasses(new Class[] {Test16_WebTestFactory.class});
		testNG.run();
	}
	
}
  • 直接在編輯器中運行Test16_run.java,即可以得到與4一樣的結果

  • 6、@Factory 也可以配合 dataprovider 使用,實現參數化


Test17_WebTestFactory.java
//@Factory 也可以配合 data provider 使用,可用於構造器或者普通方法。

package unittests;

import org.testng.annotations.DataProvider;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;
import unittests.Test16_WebTest;

public class Test17_WebTestFactory {

	@Factory(dataProvider = "dp")
	public Object[] createInstances(int n){
		Object[] result = new Object[n];
		for(int i=0; i < n; i++){
			result[i] = new Test16_WebTest(i * 10);
		}
		return result;
	}
	
	@DataProvider
	public Object[][] dp() {
		return new Object[][]{
		new Object[] { 10 },
		new Object[] { 20 },
		};
	}
	
}
  • 以xml或者代碼的方式運行Test17_WebTestFactory.java,結果如下:

access the web page88
access the web page89

===============================================
suite
Total tests run: 30, Failures: 0, Skips: 0
===============================================

13、類級別的注解


Test18.java

package unittests;

import org.testng.annotations.Test;

@Test
public class Test18 {

	public void test1() {
		System.out.println("access the test1");
	}
	
	public void test2(){
		System.out.println("access the test2");
	}
	
}
  • Eclipse中直接運行Test18.java,結果為:

[RemoteTestNG] detected TestNG version 6.14.3
access the test1
access the test2
PASSED: test1
PASSED: test2

===============================================
    Default test
    Tests run: 2, Failures: 0, Skips: 0
===============================================

運行結果:test1,test2 都被執行了,也就是說類級別的@Test 注解會把該類的所有
方法都當作測試方法,不管是否有注解。這種情況下,依舊可為單個類方法進行注解以添加
其他屬性,比如歸屬的組別。

14、通過注解實現多線程測試


MultiThreadOnAnnotion.java

package com.units.mytest.multiThread;

import org.testng.annotations.Test;

public class MultiThreadOnAnnotion {

/*  invocationCount----表示所有線程總共執行的次數

    threadPoolSize-----表示線程池的內線程的個數

    timeOut-------超時時間-毫秒*/
    @Test(invocationCount = 10,threadPoolSize = 3,timeOut = 1000)
    public void test(){
        System.out.println(1);
        System.out.printf("Thread Id : %s%n",Thread.currentThread().getId());
    }
}
  • 這里是,啟用3個線程並發,將當前方法執行10次,10次總共耗時不超過1s,超過則異常;
  • timeOut可以不設置,即可以不定超時時間。
  • 注:啟用的線程數可能不等於我們配置的線程池個數,因為線程的個數還取決於硬件CPU的支持。
  • 執行結果為:

===============================================
Default Suite
Total tests run: 10, Failures: 0, Skips: 0
===============================================

15、多線程測試-xml文件實現

  • 1、方法級別多線程

MultiThreadOnXml.java

package com.units.mytest.multiThread;

import org.testng.annotations.Test;

public class MultiThreadOnXml {
    //打印出當前方法的線程id
    @Test
    public void test1(){
        System.out.printf("Thread Id : %s%n",Thread.currentThread().getId());
    }

    @Test
    public void test2(){
        System.out.printf("Thread Id : %s%n",Thread.currentThread().getId());
    }

    @Test
    public void test3(){
        System.out.printf("Thread Id : %s%n",Thread.currentThread().getId());
    }

}

在resources中新建multiThread.xml,如下


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!-- parallel是指多線程級別,這里是方法級別的多線程;thread-count:並發的多線程數,這里是2 -->
<!-- methods級別:所有用例都可以在不同的線程下去執行
xml文件配置這種方式不能指定線程池,只有方法上才可以指定線程池-->
<suite name="thread" parallel="methods" thread-count="2">
    <test name="demo1">
        <classes>
            <class name="com.units.mytest.multiThread.MultiThreadOnXml"/>
        </classes>

    </test>
</suite>
  • multiThread.xml運行結果為

    以上是將所有用例,分配給兩個線程同時執行。

  • 2、tests級別多線程

  • 不同的test tag(如 )下的用例可以在不同的線程下並發執行;
  • 相同的test tag下的用例只能在同一個線程中去執行。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!--
tests級別:不同的test tag下的用例可以在不同的線程下執行
        相同的test tag下的用例只能在同一個線程中去執行
classs級別:相同的class tag 下的用例在同一個線程中執行
            不同的class tag 下的用例可以在不同的線程中執行
methods級別:所有用例都可以在不同的線程下去執行

thread-count:代表了最大並發線程數

xml文件配置這種方式不能指定線程池,只有方法上才可以指定線程池
-->
<suite name="thread" parallel="tests" thread-count="2">
    <test name="demo1">
        <classes>
            <class name="com.units.mytest.multiThread.MultiThreadOnXml"/>
        </classes>
    </test>
</suite>

xml運行結果為

雖然這里分配了兩個線程,但因只有一個test標簽,所以只啟用一個線程執行。

  • 3、classes級別多線程
  • 相同的class tag(如<class name="com.units.mytest.multiThread.MultiThreadOnXml"/>)下的用例在同一個線程中執行;
  • 不同的class tag 下的用例可以在不同的線程中並發執行。

<suite name="thread" parallel="classes" thread-count="3">

    <test name = "demo1">
        <classes >
            <!--這里有兩個class標簽,同一個class標簽里的所有用例,在同一個線程里執行-->
            <class name="com.units.mytest.multiThread.MultiThreadOnXml"/>
            <class name="com.units.mytest.BasicAnnotation"/>
        </classes>
    </test>

</suite>

執行結果為:


beforeClass這是在類運行之前運行的方法
Thread Id : 11BeforeMethon這是在測試方法之前運行的

 這是測試用例1
Thread Id : 12
AfterMethod這是在測試方法之后運行的
Thread Id : 11BeforeMethon這是在測試方法之前運行的

 這是測試用例2
Thread Id : 12
AfterMethod這是在測試方法之后運行的
Thread Id : 11
afterClass這是在類運行之后運行的方法
AfterSuite測試套件,在當前測試套件下所有類運行結束之后運行一次,包含該套件下所有類

===============================================
thread
Total tests run: 5, Failures: 0, Skips: 0
===============================================

16、超時測試


TimeOutTest.java

package com.units.mytest;

import org.testng.annotations.Test;

public class TimeOutTest {

    @Test(timeOut = 3000)//單位為毫秒值
    public void testSuccess() throws InterruptedException {
        Thread.sleep(2000);   //等待2s
    }

    @Test(timeOut = 2000)
    //throws InterruptedException:在當前方法拋出中斷異常,如有的話
    public void testFailed() throws InterruptedException {
        Thread.sleep(3000);
    }
}
  • 執行結果為:

    第一個成功,第二個失敗,第二個超出了期望超時時間2s

TestNG自帶測試報告、ReportNG與ExtentReport效果對比

開啟TestNG自帶的測試報告

在idea中創建testNG的工程,運行結束不會生產test-output目錄和生成測試報告,原因是在運行配置中,沒有勾選use Default reporters

  • 設置步驟:
  • 1、點擊工具條中Run,選擇Edit Configurations,彈出運行配置對話框
  • 2、在左側選擇要設置運行的測試腳本,在左側選擇Configuration的Listeners項,勾選use default reporters前的選擇框打上對號,然后點擊ok,重新運行test,就會生產test-output文件了。(可針對某個測試腳本的運行輸出測試報告,其他不設置的不輸出報告)
    如下:

  • ReportNg測試報告展示

  • ExtentReport測試報告效果(深度報告)

TestNg斷言


package com.tester.extend.demo;

import org.testng.Assert;
import org.testng.Reporter;
import org.testng.annotations.Test;

public class TestMeThodsDemo {

    @Test
    public void test1(){
        Assert.assertEquals(1,2);
    }

    @Test
    public void test2(){
        Assert.assertEquals(1,1);
    }

    @Test
    public void test3(){
        Assert.assertEquals("aaa","aaa");
    }

    @Test
    public void logDemo(){
        Reporter.log("這是我們自己寫的日記");   //報告日志
        throw new RuntimeException("這是我們自己的運行時異常");
    }
}

附:在idea中創建module(模塊)

  • 1、在項目下新建pom.xml文件,作為整個項目的pom文件,對整個項目進行一些基本的默認定義
  • 此時,可能不能自動識別pom文件,可用如下方法解決:
  • 選中pom.xml文件,右鍵-" add as maven project"識別成后,pom文件前面是個字母 m,如下
  • 2、在項目下新建module(單元模塊)




    完成后,會自動在當前目錄下的pom文件中,添加一個module,即當前創建的單元模塊。


免責聲明!

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



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