TestNG单元测试与使用详解

TestNG的基本注解与执行顺序

在类里编辑程序时,在@Test后面,摁 alt+回车,选择对应的插件,可以把目前用到的插件自动添加到 pom.xml 文件中,如下面的testng,每摁一次,就多添加一次
TestNG单元测试与使用详解

当使用的类里,用到的方法没有导入类包时,可以在 类的大括号 后面,摁 alt+回车 ,可以自动导入目前的类中,需要用到的类包,如 import org.testng.annotations.Test;
TestNG单元测试与使用详解

1、注解实战BeforeMethod和AfterMethod

  • @BeforeMethod,在每次测试方法运行之前运行;
  • @AfterMethod,在每次测试方法运行之后运行。
  • 比如:
    TestNG单元测试与使用详解

  • 运行结果为:
    TestNG单元测试与使用详解

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这是在类运行之后运行的方法");
    }
}
  • 运行结果为:
    TestNG单元测试与使用详解

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测试套件,在当前测试套件下所有类运行结束之后运行一次,包含该套件下所有类");
    }
}
  • 运行结果为:
    TestNG单元测试与使用详解

4、套件测试

  • 1、在包下,新建名为suite的包,用来管理测试套件
    TestNG单元测试与使用详解
  • 2、在测试套件包下,新建一个测试套件配置类,用来对所有测试套件进行配置
    TestNG单元测试与使用详解
  • 如,SuiteConfig,里面放的是运行测试套件前,需要运行的方法,是公共方法。
    TestNG单元测试与使用详解
  • 3、再新建测试类,如LoginTest,里面存放的是@test底下的方法,即需要运行的测试方法。
    TestNG单元测试与使用详解
  • Resources里面,存放的是@test的配置文件XML
    TestNG单元测试与使用详解

  • 4、新建XML配置文件
    TestNG单元测试与使用详解

  • 5、编辑相关文件
    TestNG单元测试与使用详解


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>
  • 运行结果为:
    TestNG单元测试与使用详解

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 执行");
    }
}
  • 运行结果为:
    TestNG单元测试与使用详解

6、组测试中的方法分组测试(方法组)

  • 1、创建一个groups包
    TestNG单元测试与使用详解
  • 2、在组包下,创建方法分组测试类 GroupsOnMethod
    TestNG单元测试与使用详解
  • 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("这是客户端组运行之后运行的方法!!!!!");
    }
}
  • 运行结果为:
    TestNG单元测试与使用详解
    实现了分组测试。若不加@BeforeGroups("client")等,则组方法按顺序执行,不额外执行其他方法。

7、组测试中的类分组测试(按类进行分组测试)

  • 1、新建三个类
    TestNG单元测试与使用详解

  • 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>
  • 运行结果为:
    TestNG单元测试与使用详解

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();   //强制抛出一个运行时异常
    }
}
  • 运行结果为:
    TestNG单元测试与使用详解
    测试结果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:] 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的功能大相径庭,但是在多次执行的问题上常常理不清。

首先,必须明确,使用@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);
}

首先,使用@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
    TestNG单元测试与使用详解

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,结果为:
    TestNG单元测试与使用详解

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的结果为
    TestNG单元测试与使用详解

  • 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运行结果为
    TestNG单元测试与使用详解
    以上是将所有用例,分配给两个线程同时执行。

  • 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运行结果为
TestNG单元测试与使用详解
虽然这里分配了两个线程,但因只有一个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);
    }
}
  • 执行结果为:
    TestNG单元测试与使用详解
    第一个成功,第二个失败,第二个超出了期望超时时间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文件了。(可针对某个测试脚本的运行输出测试报告,其他不设置的不输出报告)
    如下:
    TestNG单元测试与使用详解
    TestNG单元测试与使用详解

  • ReportNg测试报告展示
    TestNG单元测试与使用详解

  • ExtentReport测试报告效果(深度报告)
    TestNG单元测试与使用详解

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,如下
    TestNG单元测试与使用详解
  • 2、在项目下新建module(单元模块)
    TestNG单元测试与使用详解
    TestNG单元测试与使用详解
    TestNG单元测试与使用详解
    TestNG单元测试与使用详解
    完成后,会自动在当前目录下的pom文件中,添加一个module,即当前创建的单元模块。

相关推荐