TestNG详解

TestNG

1、概述

TestNG是一个开源的自动化测试框架,它受JUnit和NUnit启发,其中“NG”即表示Next Generation,其功能更强大使用更方便。

1.1、Maven依赖

<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>6.10</version>
    <scope>test</scope>
</dependency>

1.2、功能

  • 支持注释
  • TestNG使用更多Java和OO功能。
  • 支持测试集成类(例如,默认情况下,不需要为每个测试方法创建新的测试类实例)。
  • 将编译时测试代码与运行时配置/数据信息分开。
    灵活的运行时配置
  • 支持’测试组’。 编译完测试后,可以让TestNG运行所有“前端”测试,或“快速”,“慢速”,“数据库”测试等。
  • 支持相关测试方法,并行测试,负载测试和部分故障。
  • 灵活的插件API。
  • 支持多线程测试。

2、启动方式

TestNG可以通过几种不同的方式调用 -

  • 使用testng.xml文件
  • With ANT
  • 从命令行

测试类:

package pers.zhang.testng;

import org.testng.annotations.Test;

public class MyTest {
    
    @Test
    public void test() {
        System.out.println("test");
    }
}

2.1、Idea直接运行

使用Idea直接运行会使用自动生成的testng.xml文件:
在这里插入图片描述

2.2、XML调用

my-test.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="My_Suite">
    <test name="my_test">
        <classes>
            <class name="pers.zhang.testng.MyTest">
                <methods>
                    <include name="test"/>
                </methods>
            </class>
        </classes>
    </test>
</suite> 
1)IDEA直接运行xml文件

在IDE,例如IntellJ IDEA中,鼠标右击testng.xml文件,选择run即可:
在这里插入图片描述

2)、maven使用surefire插件

maven使用surefire这个插件进行测试,可以执行testng或者Junit脚本,需要在pom文件中,指明testng.xml文件的位置。

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19</version>
            <configuration>
                <suiteXmlFiles>
                    <!-- 该文件位于工程根目录时,直接填写名字,其它位置要加上路径 -->
                    <suiteXmlFile>src/test/java/pers/zhang/testng/my-test.xml</suiteXmlFile>
                </suiteXmlFiles>
            </configuration>
        </plugin>
    </plugins>
</build>

启动,进入工程根目录:

mvn test -Dmy-test.xml

结果如下:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running TestSuite
test
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.241 sec - in TestSuite

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

3、XML配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="My_Suite">
    <listeners>
        <listener class-name="pers.zhang.junit.MyListener"></listener>
    </listeners>
    <method-selectors>
        <method-selector>
            <script language="">some script</script>
        </method-selector>
        <method-selector>
            <selector-class name="pers.zhang.testng.MySelector"/>
        </method-selector>
    </method-selectors>
    <parameter name="xx" value="xx"/>
    <suite-files>
        <suite-file path="my-test.xml"/>
    </suite-files>
    <test name="my_test">
        <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>
            <dependencies>
                <group name="b" depends-on="functest">
                </group>
            </dependencies>
        </groups>
        <packages>
            <package name = "packageName.*" >
                <include name="xx"/>
                <exclude name="yy"/>
            </package>
        </packages>
        <classes>
            <class name="pers.zhang.testng.MyTest">
                <methods>
                    <include name="test"/>
                    <exclude name="a"/>
                    <parameter name="xx" value="value"/>
                </methods>
            </class>
        </classes>
    </test>
</suite>

3.1、suite标签

testNG.xml文件的最外层标签即suite,即测试套件。

suite标签的子标签如下:

  • test
  • groups
  • listeners
  • method-selectors
  • packages
  • parameter
  • suite-files

suite标签的属性如下:

<suite name="My_Suite"
       junit="false"
       verbose="5"
       parallel="tests"
       configfailurepolicy="skip"
       thread-count="5"
       skipfailedinvocationcounts="false"
       data-provider-thread-count="10"
       group-by-instances="false"
       preserve-order="true" 
       allow-return-values="false">
</suite>
  • name:必选,值可以自行设定,此名字会在testNG的报告中看到
  • junit:默认false,是否在JUnit模式下运行
  • verbose:指定testNG报告的详细程度,从0开始到10,其中10为最详细,默认生成的xml此属性值为1
  • parallel:枚举值,指代运行方式
    • none:默认值,即串行运行方式
    • methods:方法层级,若为此值,则该suite下所有的测试方法都将进行多线程,即测试用例级别的多线程。如果用例之间有依赖,则执行顺序会按照设定的依赖来运行
    • tests:TestNG将在同一线程中运行相同的<Test>标签中的所有方法,每个<test>标签都将处于一个单独的线程中,这允许您将不是线程安全的所有类分组在同一个<test>中,并保证它们都将在同一个线程中运行,同时利用TestNG使用尽可能多的线程运行测试
    • classes:类级别并发,即TestNG会将该suite下每个class都将在单独的线程中运行,同一个class下的所有用例都将在同一个线程中运行
    • instances:实例级别,即TestNG将在同一线程中运行同一实例中的所有方法,两个不同实例上的两个方法将在不同的线程中运行。
  • parent-module:指定父模块
  • guice-stage:创建父注入器的阶段
    • DEVELOPMENT:默认
    • PRODUCTION
    • TOOL
  • configfailurepolicy:指定是在一次失败后继续尝试Before/After/class方法,还是跳过剩余的
    • skip:跳过
    • continue:继续
  • thread-count:默认5,此属性用于指定线程数,按照需要输入,需要parallel参数非none时才可以添加
  • annotations:此项为注解的级别,为methods级别和class级别,一般不用设置
  • time-out:此属性用于指定超时时间,该suite下所有的用例的超时时间
  • skipfailedinvocationcounts:默认false:是否跳过失败的调用
  • data-provider-thread-count:默认10,给出用于并行数据提供程序的线程池的大小。
  • object-factory:实现IObjectFactory的类,该类将用于实例化测试对象。
  • group-by-instances:默认false,此项用于那些有依赖的方法,且被依赖的对象有多个重载对象,因为如果是依赖方法,且该方法有多个重载方法,则默认是会将所有重载方法都跑完再运行被依赖方法,但有时候我们不想这样,则将此项设置为true即可
  • preserve-order:默认true,值可输入true或者false,如果为true,则用例执行会按照在xml中的顺序执行,否则会乱序执行,不添加此属性的话默认是按顺序执行的
  • allow-return-values:默认false,如果为true,则返回值的测试也将运行

3.2、suite-files标签

suite-files/suite-file用于引入其它xml,path属性指定xml文件路径。

<suite-files>
    <suite-file path="my-test.xml"/>
</suite-files>

3.3、parameter标签

parameter标签可以在<suite><test>级别定义参数。在<test>级别定义的参数覆盖<suite>中同名的参数。参数用于将Java方法参数链接到此处定义的实际值。

<parameter name="xx" value="xx"/>

3.4、method-selectors标签

方法选择器定义用于选择要运行的方法的用户类。他们需要实现org.testng.IMethodSelector,或者直接写脚本。

子标签script属性:

  • language:指定脚本语言

子标签selector-class属性:

  • name:名字
  • priority:优先级
<method-selectors>
    <method-selector>
        <script language="">some script</script>
    </method-selector>
    <method-selector>
        <selector-class name="pers.zhang.testng.MySelector"/>
    </method-selector>
</method-selectors>

3.5、test标签

子标签:

  • parameter
  • packages
  • groups
  • classes
  • method-selectors

属性:(含义与suite相同)

  • name
  • junit
  • verbose
  • parallel
  • thread-count
  • annotations
  • time-out
  • enabled
  • skipfailedinvocationcounts
  • preserve-order
  • group-by-instances
  • allow-return-values
<test name="my_test"
      junit="false"
      verbose="5"
      parallel="tests"
      thread-count="5"
      skipfailedinvocationcounts="false"
      group-by-instances="false"
      preserve-order="true"
      allow-return-values="false">
    <method-selectors></method-selectors>
    <parameter name="xx" value="xx"/>
    <groups></groups>
    <packages></packages>
    <classes>
        <class name="pers.zhang.testng.MyTest">
            <methods>
                <include name="test"/>
            </methods>
        </class>
    </classes>
</test>
1)、选择一个包中的全部测试脚本(包含子包)
<test name = "allTestsInAPackage" >
   <packages>
      <package name = "whole.path.to.package.* />
   </packages>
</test>
2)、选择一个类中的全部测试脚本
<test name = "allTestsInAClass" >
   <classes>
       <class name="whole.path.to.package.className />
   </classes>
</test>
3)、选择一个类中的部分测试脚本
<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>
4)、选择一个包中的某些组
<test name = "includedGroupsInAPackage" >
   <groups>
      <run>
         <include name = "includedGroup" />
      </run>
   </groups>
   <packages>
      <package name = "whole.path.to.package.* />
   </packages>
</test>
5)、排除一个包中的某些组
<test name = "excludedGroupsInAPackage" >
   <groups>
      <run>
         <exclude name = "excludedGroup" />
      </run>
   </groups>
   <packages>
      <package name = "whole.path.to.package.* />
   </packages>
</test>

3.6、groups标签

此标签必然是在<test>标签下的,用于标识那些组会被用于测试或者被排除在测试之外,其同级必然要包含一个<classes>标签或者<pakages>标签,用于指定groups来自于哪些包或者类。

子标签:

  • define:定义组
  • run:描述当前组规则
  • dependencies:定义组依赖
1)、define

定义一个组,子标签包括:

  • include:定义要包含在当前组中的组
<groups>
    <define name="functest">
      <include name="windows"/>
      <include name="linux"/>
</groups>
2)、run

描述当前Test的组规则,子标签包括:

  • include:定义要包含在当前组中的组
  • exclude:定义要排除在当前组中的组
<groups>
  <run>
     <include name = "includedGroupName" />
     <exclude name = "excludedGroupName" />
  </run>
</groups>
3)、dependencies

描述组依赖,子标签:

  • group:依赖的组
<test name="My suite">
  <groups>
    <dependencies>
      <group name="c" depends-on="a  b" />
      <group name="z" depends-on="c" />
    </dependencies>
  </groups>
</test>
4)、简单分组
<test name="Test1">
  <groups>
    <run>
      <include name="windows.*"/>
    </run>
  </groups>
 
  <classes>
    <class name="example1.Test1"/>
  </classes>
</test>
5)、嵌套分组
  • all:
    • functest:
      • windows
      • linux
    • checkintest
<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>

3.7、classes标签

要包含在此测试中的类的列表,子标签:

  • class:指定类
  • paramter:参数

选择一个类:

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

选择一个方法:

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

3.8、package标签

要包含在此测试中的程序包列表,子标签:

  • package:包,如果包名称以.*结尾,那么子包也会包含在内。

选择一个包及其子包:

<packages>
    <package name = "packageName.*" >
        <include name="xx"/>
        <exclude name="yy"/>
    </package>
</packages>

3.9、methods标签

此测试中包含/排除的方法列表,子标签:

  • include
  • exclude
  • parameter
<classes>
    <class name="pers.zhang.testng.MyTest">
        <methods>
            <include name="test"/>
            <exclude name="a"/>
            <parameter name="xx" value="value"/>
        </methods>
    </class>
</classes>

3.10、listeners标签

将传递给TestNG的侦听器列表,子标签:

  • listener
<listeners>
    <listener class-name="pers.zhang.junit.MyListener"></listener>
</listeners>

4、TestNG注解

4.1、@Test

@Test将类或方法标记为测试的一部分,常用属性如下:

  • groups:指定该类/方法所属的组的列表
  • enabled:默认true,是否启用了此类/方法上的方法
  • dependsOnGroups:此方法所依赖的组的列表。其中一个组的每个方法成员都保证在此方法之前被调用。此外,如果这些方法中的任何一个不是SUCCESS,则此测试方法将不会运行,并将被标记为SKIP
  • dependsOnMethods:此方法所依赖的方法的列表。无法保证所依赖方法的运行顺序,但可以保证在运行包含此注释的测试方法之前,所有这些方法都将运行。此外,如果这些方法中的任何一个不是SUCCESS,则此测试方法将不会运行,并将被标记为SKIP。如果其中一些方法已重载,则所有重载的版本都将运行
  • timeOut:此测试应花费的最大毫秒数。如果在这段时间之后没有返回,它将被标记为FAIL
  • invocationTimeOut:此test方法的调用总数应花费的最大毫秒数。如果未在此方法上指定invocationCount属性,则将忽略此注释。如果在这段时间之后没有返回,它将被标记为FAIL
  • invocationCount:默认1,应调用此方法的次数
  • threadPoolSize:此方法的线程池的大小。该方法将根据invocationCount的指定从多个线程调用。注意:如果未指定invocationCount,则忽略此属性
  • successPercentage:默认100,此方法预期的成功百分比
  • dataProvider:此测试方法的数据提供程序的名称
  • dataProviderClass:默认Object.class,在其中查找数据提供程序的类。如果未指定,则将在当前测试方法的类或其一个超类上查找数据提供程序。如果指定了此属性,则数据提供程序方法在指定的类上需要是静态的
  • alwaysRun:默认false,如果设置为true,则此测试方法将始终运行,即使它依赖于失败的方法。如果此测试不依赖于任何方法或组,则会忽略此属性
  • description:此方法的描述。使用的字符串将出现在HTML报告中,如果verbose>=2,也会出现在标准输出中
  • expectedExceptions:测试方法预期抛出的异常列表。如果没有抛出异常或与此列表中的异常不同,则此测试将被标记为失败
  • expectedExceptionsMessageRegExp:默认.*,如果指定了expectedExceptions,则其消息必须与此属性中指定的正则表达式匹配
  • suiteName:此测试类应放在其中的套件的名称。如果@test不在类级别,则忽略此属性
  • testName:此测试类应放在其中的测试的名称。如果@test不在类级别,则忽略此属性
  • sequential:默认false,使用单线程
  • singleThreaded:默认false,如果设置为true,则该测试类上的所有方法都保证在同一个线程中运行,即使当前正在使用parallel=“true”运行测试也是如此。此属性只能在类级别使用,如果在方法级别使用,则会被忽略
  • retryAnalyzer:默认Class.class,如果应该重试测试,则应该调用以测试的类的名称
  • skipFailedInvocations:默认false,如果为true并且invocationCount的值>1,则失败后的所有调用都将标记为SKIP,而不是FAIL
  • ignoreMissingDependencies:如果设置为true,即使它所依赖的方法丢失或被排除,该测试也将运行
  • priority:默认0,计划优先级。优先级较低的项目将首先安排

4.2、测试周期注解

  • @BeforeSuite:在该套件的所有测试都运行在注释的方法之前,仅运行一次
  • @AfterSuite:在该套件的所有测试都运行在注释方法之后,仅运行一次
  • @BeforeClass:在调用当前类的第一个测试方法之前运行,注释方法仅运行一次
  • @AfterClass:在调用当前类的第一个测试方法之后运行,注释方法仅运行一次
  • @BeforeTest:注释的方法将在属于test标签内的类的所有测试方法运行之前运行
  • @AfterTest:注释的方法将在属于test标签内的类的所有测试方法运行之后运行
  • @BeforeGroups:配置方法将在之前运行组列表。 此方法保证在调用属于这些组中的任何一个的第一个测试方法之前不久运行
  • @AfterGroups:此配置方法将在之后运行组列表。该方法保证在调用属于任何这些组的最后一个测试方法之后不久运行
  • @BeforeMethod:注释方法将在每个测试方法之前运行
  • @AfterMethod:注释方法将在每个测试方法之后运行

执行顺序如下:

public class NewTest {

    @Test(groups = "group1")
    public void test1() {
        System.out.println("test1 from group1");
    }

    @Test(groups = "group1")
    public void test2() {
        System.out.println("test2 from group1");
    }

    @Test(groups = "group2")
    public void test3() {
        System.out.println("test3 from group2");
    }

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

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

    @BeforeClass
    public void beforeClass() {
        System.out.println("beforeClass");
    }

    @AfterClass
    public void afterClass() {
        System.out.println("afterClass");
    }

    @BeforeSuite
    public void beforeSuite() {
        System.out.println("beforeSuite");
    }

    @AfterSuite
    public void afterSuite() {
        System.out.println("afterSuite");
    }

    //只针对group1有效,即test1和test2
    @BeforeGroups(groups = "group1")
    public void BeforeGroups() {
        System.out.println("BeforeGroups");
    }

    //只针对group1有效,即test1和test2
    @AfterGroups(groups = "group1")
    public void AfterGroups() {
        System.out.println("AfterGroups");
    }

    @BeforeMethod
    public void beforeMethod() {
        System.out.println("beforeMethod");
    }

    @AfterMethod
    public void AfterMethod() {
        System.out.println("AfterMethod");
    }

}

输出次序:

beforeSuite
beforeTest
beforeClass
BeforeGroups
	beforeMethod
		test1 from group1
	AfterMethod
	beforeMethod
		test2 from group1
	AfterMethod
AfterGroups
beforeMethod
	test3 from group2
AfterMethod
afterClass
afterTest
afterSuite

4.3、@DataProvider

标记一种方法来提供测试方法的数据。 注释方法必须返回一个Object [] [],其中每个Object []可以被分配给测试方法的参数列表。 要从该DataProvider接收数据的@Test方法需要使用与此注释名称相等的dataProvider名称。

@DataProvider注解属性如下:

  • name:名称
  • parallel:默认false,是否应并行运行此数据提供程序
  • indices:要从此数据提供程序运行的索引,默认值:all

4.4、@Factory

将方法标记为工厂,返回将由TestNG用作Test类的对象。 该方法必须返回Object []

示例一:

//测试类
public class TestNGFactoryTest {

    private Integer number;
    private static int num;

    public TestNGFactoryTest(Integer number) {
        this.number = number;
    }

    @Test
    public void testCount() {
        num++;
        System.out.println("num " + num + " is " + this.number);
    }
}

//工厂类
public class TestNgFactory {

    @Factory
    public Object[] factoryMethod() {
        Object[] result = new Object[10];
        for (int i = 0; i < 10; i++) {
            result[i] = new TestNGFactoryTest(i * 10);
        }
        return result;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="My_Suite">
    <test name="factory_test">
        <classes>
            <class name="pers.zhang.testng.TestNgFactory"/>
        </classes>
    </test>
</suite>

运行结果:

num 1 is 60
num 2 is 70
num 3 is 90
num 4 is 30
num 5 is 20
num 6 is 80
num 7 is 40
num 8 is 0
num 9 is 10
num 10 is 50

示例二:

//测试类一
public class HomePageTest {

    private String userId;
    private String userName;

    public HomePageTest(String userId, String userName) {
        this.userId = userId;
        this.userName = userName;
    }

    @Test
    public void testHomeLogin() {
        System.out.println(userId + " = " + userName + " are loggin home page");
    }
}

//测试类二
public class LoginPageTest {

    private String userId;
    private String userName;

    public LoginPageTest(String userId, String userName) {
        this.userId = userId;
        this.userName = userName;
    }

    @Test
    public void testLogin() {
        System.out.println(userId + " = " + userName + " are loggin sub page");
    }
}

//工厂类
public class FactoryAndDataProvider {

    @DataProvider(name = "dataProvider")
    public Object[][] dataProvider() {
        Object[][] objects = new Object[2][2];
        objects[0][0] = "user1";
        objects[0][1] = "zhangsan";
        objects[1][0] = "user2";
        objects[1][1] = "lisi";
        return objects;
    }

    @Factory(dataProvider = "dataProvider")
    public Object[] createTest(String userId, String userName) {
        Object[] test = new Object[2];
        test[0] = new HomePageTest(userId, userName);
        test[1] = new LoginPageTest(userId, userName);
        return test;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="My_Suite">
    <test name="factory_test2">
        <classes>
            <class name="pers.zhang.testng.FactoryAndDataProvider"/>
        </classes>
    </test>
</suite>

输出:

user1 = zhangsan are loggin home page
user2 = lisi are loggin home page
user1 = zhangsan are loggin sub page
user2 = lisi are loggin sub page

注意:从上面两个示例的结果可以看到,最后结果并不是有序的

4.5、@Listeners

在测试类上定义监听器,监听器必须扩展自org.testng.ITestNGListener

//测试类
@Listeners(value = {pers.zhang.testng.MyListener.class})
public class ListenerTest {


    @Test
    public void test1() {
        System.out.println("test1");
        Assert.assertTrue(true);
    }

    @Test
    public void test2() {
        System.out.println("test2");
        Assert.assertTrue(false);
    }
}

//监听器
public class MyListener implements ITestListener {

    /**
     * 在任何测试开始时调用
     */
    @Override
    public void onTestStart(ITestResult result) {
        System.out.println("onTestStart");
    }

    /**
     * 在任何测试成功时调用
     */
    @Override
    public void onTestSuccess(ITestResult result) {
        System.out.println("onTestSuccess");
    }

    /**
     * 在任何测试失败时调用
     */
    @Override
    public void onTestFailure(ITestResult result) {
        System.out.println("onTestFailure");
    }

    /**
     * 在跳过任何测试时调用
     */
    @Override
    public void onTestSkipped(ITestResult result) {
        System.out.println("onTestSkipped");
    }

    /**
     * 在每次测试失败但在成功百分比内时调用
     */
    @Override
    public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
        System.out.println("onTestFailedButWithinSuccessPercentage");
    }

    /**
     * 在实例化测试类之后,在调用任何配置方法之前调用。
     */
    @Override
    public void onStart(ITestContext context) {
        System.out.println("onStart");
    }

    /**
     * 在运行完所有测试并调用了它们的所有Configuration方法后调用。
     */
    @Override
    public void onFinish(ITestContext context) {
        System.out.println("onFinish");
    }
}

输出:

onStart
onTestStart
test1
onTestSuccess
onTestStart
test2
onTestFailure

java.lang.AssertionError: expected [true] but found [false]
Expected :true
Actual   :false
<Click to see difference>
...
onFinish

如果不使用注解,等价的xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="My_Suite">
    <listeners>
        <listener class-name="pers.zhang.testng.MyListener"></listener>
    </listeners>
    <test name="listener_test">
        <classes>
            <class name="pers.zhang.testng.ListenerTest"/>
        </classes>
    </test>
</suite>

4.6、@Parameters/@Optional

描述如何将xml中的参数传递给@Test方法,@Optional用于指定默认值。

//测试类
public class ParametersTest {

    @Parameters(value = {"name", "age", "height"})
    @Test
    public void test(String name, Integer age, @Optional("1.77") Double height) {
        System.out.println(name);
        System.out.println(age);
        System.out.println(height);
    }
}

xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="My_Suite">
    <parameter name="name" value="tom"/>
    <parameter name="age" value="18"/>
    <test name="parameters_test">
        <classes>
            <class name="pers.zhang.testng.ParametersTest"/>
        </classes>
    </test>
</suite>

输出:

tom
18
1.77

5、套件测试

test suite是一组测试用例,用于测试软件程序的行为或一组行为。 在TestNG中,我们无法在测试源代码中定义套件,但它由一个XML文件表示,因为套件是执行的特征。 它还允许灵活配置要运行的tests 。 套件可以包含一个或多个测试,并由“suite”标签定义。

//被测试类
public class MessageUtil {

    private String message;

    public MessageUtil(String message) {
        this.message = message;
    }

    public String printMessage() {
        System.out.println(message);
        return message;
    }

    public String salutationMessage() {
        message = "Hi!" + message;
        System.out.println(message);
        return message;
    }
}


//测试类1
public class SuiteTest1 {

    @Test
    public void testPrintMessage() {
        MessageUtil messageUtil = new MessageUtil("tom");
        Assert.assertEquals("tom", messageUtil.printMessage());
    }
}

//测试类2
public class SuiteTest2 {

    @Test
    public void testSalutationMessage() {
        MessageUtil messageUtil = new MessageUtil("jerry");
        Assert.assertEquals("Hi!jerry", messageUtil.salutationMessage());
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="suite_test1">
        <classes>
            <class name="pers.zhang.testngdemo.SuiteTest1"/>
        </classes>
    </test>
    <test name="suite_test2">
        <classes>
            <class name="pers.zhang.testngdemo.SuiteTest2"></class>
        </classes>
    </test>
</suite>

运行测试输出:

[TestNG] Running:
tom
Hi!jerry
===============================================
Suite
Total tests run: 2, Failures: 0, Skips: 0
===============================================

6、忽略测试

有时候,我们的代码没有准备就绪并且编写测试用例来测试该方法/代码失败。 在这种情况下,注释@Test(enabled = false)有助于禁用此测试用例。

如果使用@Test(enabled = false)注释测试方法,则会绕过未准备好测试的测试用例。

//被测试类
public class IgnoreTestDemo {
    
    public void a() {
        System.out.println("aaa");
    }

    public void b() {
        System.out.println("bbb");
    }
}

//测试类
public class IgnoreTest {

    private IgnoreTestDemo demo = new IgnoreTestDemo();

    @Test
    public void test1() {
        demo.a();
    }

    @Test(enabled = false)
    public void test2() {
        demo.b();
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="ignore_test">
        <classes>
            <class name="pers.zhang.testngdemo.IgnoreTest"/>
        </classes>
    </test>
</suite>

运行测试输出:

[TestNG] Running:
aaa
===============================================
Suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================

7、分组测试

组测试是TestNG中的一项新的创新功能,在JUnit框架中不存在。 它允许您将方法分配到适当的部分并执行复杂的测试方法分组。

您不仅可以声明属于组的方法,还可以指定包含其他组的组。 然后,可以调用TestNG并要求其包含一组特定的组(或正则表达式),同时排除另一组。

组测试为您的测试分区提供了最大的灵活性,如果您想要连续运行两组不同的测试,则不需要重新编译任何内容。

组在testng.xml文件中指定,可以在<test><suite>标记下找到。<suite>标记中指定的组应用于下面的所有<test>标记。请注意,组在这些标签中是累积的:如果您在<suite>中指定组“a”,在<test>中指定“b”,则“a”和“b”都将包括在内。

7.1、简单分组

测试类中通过@Test注解的groups属性指定分组:

public class SimpleGroupTest {

    //test1属于a和b两个组
    @Test(groups = {"a", "b"})
    public void test1() {
        System.out.println("test1");
    }

    //test2属于a和b两个组
    @Test(groups = {"a", "b"})
    public void test2() {
        System.out.println("test2");
    }

    //test3属于a组
    @Test(groups = {"a"})
    public void test3() {
        System.out.println("test3");
    }
}

xml中通过groups标签指定分组:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="simple_group_test">
        <!-- 指定只运行b组 -->
        <groups>
            <run>
                <include name="b"/>
            </run>
        </groups>
        <classes>
            <class name="pers.zhang.testngdemo.SimpleGroupTest"/>
        </classes>
    </test>
</suite>

运行测试输出:

test1
test2

如果指定运行a组,则3个测试方法全部运行。

7.2、正则匹配分组

public class RegularGroupTest {

    @Test(groups = { "windows.checkintest"} )
    public void testWindowsOnly() {
        System.out.println("windows.checkintest");
    }

    @Test(groups = {"linux.checkintest"} )
    public void testLinuxOnly() {
        System.out.println("linux.checkintest");
    }

    @Test(groups = { "windows.functest"} )
    public void testWindowsToo() {
        System.out.println("windows.functest");
    }

}

正则匹配分组:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="regular_test">
        <groups>
            <run>
                <include name="window.*"/>
            </run>
        </groups>
        <classes>
            <class name="pers.zhang.testngdemo.RegularGroupTest"/>
        </classes>
    </test>
</suite>

运行结果:

windows.checkintest
windows.functest

7.3、嵌套分组

public class NestedGroupTest {

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

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

    @Test(groups = {"c"})
    public void test3() {
        System.out.println("test3");
    }

    @Test(groups = {"d"})
    public void test4() {
        System.out.println("test4");
    }
}

定义嵌套分组:

  • g2
    • c
    • g1
      • a
      • b
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="simple_group_test">
        <groups>
            <define name="g1">
                <include name="a"/>
                <include name="b"/>
            </define>
            <define name="g2">
                <include name="g1"/>
                <include name="c"/>
            </define>
            <run>
                <include name="g2"/>
                <exclude name="d"/>
            </run>
        </groups>
        <classes>
            <class name="pers.zhang.testngdemo.NestedGroupTest"/>
        </classes>
    </test>
</suite>

运行测试输出:

test1
test2
test3

7.4、类级别定义分组

@Test(groups = {"father"})
public class ClassGroupTest {

    @Test(groups = {"son"})
    public void test1() {
        System.out.println("test1");
    }
    
    @Test
    public void test2() {
        System.out.println("test2");
    }
}
  • 方法test1属于fatherson两个组
  • 方法test2只属于father
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="ignore_test">
        <groups>
            <run>
                <include name="father"/>
                <exclude name="son"/>
            </run>
        </groups>
        <classes>
            <class name="pers.zhang.testngdemo.ClassGroupTest"/>
        </classes>
    </test>
</suite>

运行测试输出:

test2

8、异常测试

TestNG提供了跟踪代码异常处理的选项。 您可以测试代码是否抛出所需的异常。 这里的expectedExceptions参数与@Test注释一起使用。

public class ExceptionTest {
    
    @Test(expectedExceptions = ArithmeticException.class)
    public void test() {
        int a = 0;
        int b = 2 / a;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="exception_test">
        <classes>
            <class name="pers.zhang.testngdemo.ExceptionTest"/>
        </classes>
    </test>
</suite>

9、超时测试

“超时”表示如果单元测试花费的时间超过指定的毫秒数,那么TestNG将会中止它并将其标记为失败。此项常用于性能测试。如下为一个范例:

public class TimeOutTest {

    @Test(timeOut = 5000)
    public void testThisShouldPass() throws InterruptedException {
        Thread.sleep(4000);
    }

    @Test(timeOut = 1000)
    public void testThisShouldFail() {
        while (true){
            // do nothing
        }

    }

}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="timeout_test">
        <classes>
            <class name="pers.zhang.testngdemo.TimeOutTest"/>
        </classes>
    </test>
</suite>

运行测试输出:
在这里插入图片描述

10、参数化测试

在大多数情况下,您会遇到业务逻辑需要大量测试的场景。 参数化测试允许开发人员使用不同的值一次又一次地运行相同的测试。

TestNG可以通过两种不同的方式将参数直接传递给测试方法:

  • 使用testng.xml配合@Parameters
  • 使用@DataProvider

10.1、使用xml传递参数

TestNG将自动尝试将testng.xml中指定的值转换为您的参数类型。 以下是支持的类型:

  • String
  • int/Integer
  • boolean/Boolean
  • byte/Byte
  • char/Character
  • double/Double
  • float/Float
  • long/Long
  • short/Short
public class XmlParamTest {

    @Test
    @Parameters({"name", "age"})//指定xml中传递的参数名称
    public void test(String name, Integer age) {
        System.out.println("name:" + name);
        System.out.println("age:" + age);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="timeout_test">
        <!-- 定义要传递的参数 -->
        <parameter name="name" value="tom"/>
        <parameter name="age" value="18"/>
        <classes>
            <class name="pers.zhang.testngdemo.XmlParamTest"/>
        </classes>
    </test>
</suite>

运行测试输出:

name:tom
age:18

10.2、使用@DataProvider传递参数

此处需要注意,传参的类型必须要一致,且带有@DataProvider注解的函数返回的必然是Object[][],此处需要注意。

public class ProviderParamTest {

    @DataProvider(name = "studentProvider")
    public Object[][] provideData() {
        return new Object[][]{
                {"tom", 18, 1.71},
                {"jerry", 22, 1.56},
                {"mike", 26, 1.83}
        };
    }

    @Test(dataProvider = "studentProvider")
    public void test(String name, Integer age, Double height) {
        System.out.println("test start");
        System.out.println("name:" + name);
        System.out.println("age:" + age);
        System.out.println("height:" + height);
        System.out.println("test end");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="timeout_test">
        <classes>
            <class name="pers.zhang.testngdemo.ProviderParamTest"/>
        </classes>
    </test>
</suite>

运行测试输出:

test start
name:tom
age:18
height:1.71
test end

test start
name:jerry
age:22
height:1.56
test end

test start
name:mike
age:26
height:1.83
test end

11、依赖性测试

有时,我们可能需要以特定顺序调用测试用例中的方法,或者可能希望在方法之间共享一些数据和状态。 TestNG支持这种依赖关系,因为它支持在测试方法之间显式依赖的声明。

TestNG允许指定依赖关系:

  • @Test注解中使用dependsOnMethods或dependsOnGroups属性
  • testng.xml中使用dependencies标签

除此之外依赖还分为hard依赖和soft依赖:

  • hard依赖:默认为此依赖方式,即其所有依赖的methods或者groups必须全部pass,否则被标识依赖的类或者方法将会被略过,在报告中标识为skip,如后面的范例所示,此为默认的依赖方式;
  • soft依赖:此方式下,其依赖的方法或者组有不是全部pass也不会影响被标识依赖的类或者方法的运行,注意如果使用此方式,则依赖者和被依赖者之间必须不存在成功失败的因果关系,否则会导致用例失败。此方法在@Test注解中需要加入alwaysRun=true即可

11.1、@Test指定依赖

1)、依赖于方法
public class DependOnMethodsTest {
    
    @Test
    public void test1() {
        System.out.println("test1");
    }

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

    //test3依赖于test2
    @Test(dependsOnMethods = {"test2"})
    public void test3() {
        System.out.println("test3");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="depend_method_test">
        <classes>
            <class name="pers.zhang.testngdemo.DependOnMethodsTest"/>
        </classes>
    </test>
</suite>

运行测试输出:

test1
test2
test3
2)、依赖于组
public class DependOnGroupsTest {

    //test1属于a组
    @Test(groups = {"a"})
    public void test1() {
        System.out.println("test1");
    }

    //test2属于a组
    @Test(groups = {"a"})
    public void test2() {
        System.out.println("test2");
    }

    //test3依赖于a组
    @Test(dependsOnGroups = {"a"})
    public void test3() {
        System.out.println("test3");
        Assert.assertTrue(false);
    }

    //test4依赖于test3
    @Test(dependsOnMethods = {"test3"})
    public void test4() {
        System.out.println("test4");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="depend_group_test">
        <classes>
            <class name="pers.zhang.testngdemo.DependOnGroupsTest"/>
        </classes>
    </test>
</suite>

运行测试输出:

test1
test2
test3

java.lang.AssertionError: expected [true] but found [false]
Expected :true
Actual   :false
<Click to see difference>...

Test ignored.
===============================================
Suite
Total tests run: 4, Failures: 1, Skips: 1
===============================================

test3失败,导致test4被跳过,属于hard依赖。

3)、soft依赖
public class SoftDependTest {

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

    //test2依赖于test1
    @Test(dependsOnMethods = {"test1"})
    public void test2() {
        System.out.println("test2");
        Assert.assertTrue(false);
    }

    //test3依赖于test2,软依赖,无论test2是否成功test3必然执行
    @Test(alwaysRun = true, dependsOnMethods = {"test2"})
    public void test3() {
        System.out.println("test3");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="soft_depend_test">
        <classes>
            <class name="pers.zhang.testngdemo.SoftDependTest"/>
        </classes>
    </test>
</suite>

运行测试输出:

test1
test2

java.lang.AssertionError: expected [true] but found [false]
Expected :true
Actual   :false
<Click to see difference>....

test3
===============================================
Suite
Total tests run: 3, Failures: 1, Skips: 0
===============================================

11.2、xml中指定依赖

public class XmlDependTest {

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

    @Test(groups = {"b"})
    public void test2() {
        System.out.println("test2");
        Assert.assertTrue(false);
    }

    @Test(groups = {"c"})
    public void test3() {
        System.out.println("test3");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="depend_group_test">
        <groups>
            <dependencies>
                <group depends-on="a" name="b"/>
                <group depends-on="b" name="c"/>
            </dependencies>
        </groups>
        <classes>
            <class name="pers.zhang.testngdemo.XmlDependTest"/>
        </classes>
    </test>
</suite>

运行测试输出:

test1
test2

java.lang.AssertionError: expected [true] but found [false]
Expected :true
Actual   :false
<Click to see difference>...

Test ignored.
===============================================
Suite
Total tests run: 3, Failures: 1, Skips: 1
===============================================

12、运行JUnit测试

TestNG可以自动识别并运行JUnit测试,因此您可以将TestNG用作所有现有测试的运行器,并使用TestNG编写新测试。 您所要做的就是将JUnit库放在TestNG类路径上,这样它就可以找到并使用JUnit类。

import org.junit.Assert;
import org.junit.Test;


public class JUnitTest {
    
    @Test
    public void test() {
        System.out.println("test");
        Assert.assertTrue(true);
    }
}

要执行JUnit测试用例,请在下面的xml中定义属性junit="true"

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="junit_test" junit="true">
        <classes>
            <class name="pers.zhang.testngdemo.JUnitTest"/>
        </classes>
    </test>
</suite>

13、测试结果/报告

报告是任何测试执行中最重要的部分,因为它可以帮助用户理解测试执行的结果,故障点和失败原因。 另一方面,记录对于密切关注执行流程或在发生任何故障时进行调试非常重要。

默认情况下,TestNG会为其测试执行生成不同类型的报告。 这包括HTML和XML报告输出。 TestNG还允许其用户编写自己的记者并将其与TestNG一起使用。 还可以选择编写自己的记录器,这些记录器在运行时由TestNG通知。

使用TestNG生成报告有两种方法:

  • Listeners:为了实现监听器类,该类必须实现org.testng.ITestListener接口。 当测试开始,结束,失败,跳过或通过时,TestNG会在运行时通知这些类。
  • Reporters:为了实现报告类,该类必须实现org.testng.IReporter接口。 整个套件运行结束时会调用这些类。 包含整个测试运行信息的对象在被调用时传递给该类。

测试类:

public class SimpleTest {

    @Test
    public void test1() {
        System.out.println("test1");
        Assert.assertTrue(true);
    }

    @Test
    public void test2() {
        System.out.println("test2");
        Assert.assertTrue(false);
    }

    @Test(dependsOnMethods = {"test2"})
    public void test3() {
        System.out.println("test3");
        Assert.assertTrue(true);
    }
}

13.1、自定义日志记录

public class LogListener extends TestListenerAdapter {

    private int m_count = 0;

    @Override
    public void onTestFailure(ITestResult tr) {
        log(tr.getName()+ "--Test method failed\n");
    }

    @Override
    public void onTestSkipped(ITestResult tr) {
        log(tr.getName()+ "--Test method skipped\n");
    }

    @Override
    public void onTestSuccess(ITestResult tr) {
        log(tr.getName()+ "--Test method success\n");
    }

    private void log(String string) {
        System.out.print(string);
        if (++m_count % 40 == 0) {
            System.out.println("");
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <listeners>
        <listener class-name="pers.zhang.testngdemo.LogListener"></listener>
    </listeners>
    <test name="log_listener_test">
        <classes>
            <class name="pers.zhang.testngdemo.SimpleTest"/>
        </classes>
    </test>
</suite>

运行测试输出:

test1
test1--Test method success

test2
test2--Test method failed

java.lang.AssertionError: expected [true] but found [false]
Expected :true
Actual   :false
<Click to see difference>...

test3--Test method skipped
Test ignored.

===============================================
Suite
Total tests run: 3, Failures: 1, Skips: 1
===============================================

13.2、自定义报告

public class CustomReporter implements IReporter {

    /**
     * @param xmlSuites:它是正在执行的testng XML中提到的套件列表
     * @param suites:包含测试执行后的套件信息。 此对象包含有关包,类,测试方法及其测试执行结果的所有信息
     * @param outputDirectory:包含将生成报告的输出文件夹路径的信息
     */
    @Override
    public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
        //对测试中包含的每个套件进行迭代
        for (ISuite suite : suites) {
            //获取套件名称
            String suiteName = suite.getName();
            //获取套件结果
            Map<String, ISuiteResult> suiteResults = suite.getResults();
            for (ISuiteResult sr : suiteResults.values()) {
                ITestContext tc = sr.getTestContext();
                System.out.println("Passed tests for suite '" + suiteName +
                        "' is:" + tc.getPassedTests().getAllResults().size());
                System.out.println("Failed tests for suite '" + suiteName +
                        "' is:" + tc.getFailedTests().getAllResults().size());
                System.out.println("Skipped tests for suite '" + suiteName +
                        "' is:" + tc.getSkippedTests().getAllResults().size());
            }
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <listeners>
        <listener class-name="pers.zhang.testngdemo.CustomReporter"></listener>
    </listeners>
    <test name="log_listener_test">
        <classes>
            <class name="pers.zhang.testngdemo.SimpleTest"/>
        </classes>
    </test>
</suite>

运行测试输出:

===============================================
Suite
Total tests run: 3, Failures: 1, Skips: 1
===============================================

Passed tests for suite 'Suite' is:1
Failed tests for suite 'Suite' is:1
Skipped tests for suite 'Suite' is:1

示例显示了一个简单的自定义报告器,它在控制台上为所述测试执行中包含的每个套件打印失败,传递和跳过测试的数量。 Reporter主要用于生成测试执行的最终报告。 该扩展可用于生成XML,HTML,XLS,CSV或文本格式文件,具体取决于报表要求。

13.3、HTML和XML报告

TestNG附带某些预定义的侦听器作为库的一部分。 默认情况下,这些侦听器会添加到任何测试执行中,并为任何测试执行生成不同的HTML和XML报告。 默认情况下,报告在名为testoutput的文件夹下生成,并且可以通过配置将其更改为任何其他文件夹。 这些报告由特定于TestNG的某些HTML和XML报告组成。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <test name="log_listener_test">
        <classes>
            <class name="pers.zhang.testngdemo.SimpleTest"/>
        </classes>
    </test>
</suite>

因为maven使用surefire插件,所以生成的报告位于surefire-reports路径下,浏览器打开index.html即可:

在这里插入图片描述

在这里插入图片描述

默认情况下,TestNG会生成多个报告,作为其测试执行的一部分。 这些报告主要包括TestNG HTML报告,TestNG可发送电子邮件报告,TestNG报告XML和JUnit报告XML文件。 这些文件可以在输出报告文件夹下找到(在本例中为surefire-reports)。

通过将属性useDefaultListeners的值设置为false可以在运行测试时禁用此默认报告生成。 使用Ant或Maven等构建工具时可以设置此属性。

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值