目录

一、TestNG的基本介绍和如何在maven中引用

1、适合测试人员使用的原因

1)比Junit涵盖功能更全面的测试框架

2)Junit更适合隔离性比较强的单元测试

3)TestNG更适合复杂的集成测试

2、TestNG的使用

pom.xml的依赖包将信息都填写好,maven框架将自动下载和使用。

二、TestNG基本注解与执行顺序实战

1、Idea创建一个项目和对应的modle,目录结构如下:

2.1 注解实战 @Test标签

1、创建包 com.course.testng

2、创建类 BasicAnnotation.java

package com.course.testng;
import org.testng.annotations.Test; public class BasicAnnotation{
// 最基本的注解,用来把方法标记成测试用例
@Test
public void testCase1(){
System.out.println("这是测试用例1");
}
}

1)@Test 需要 alt+enter 键,add ‘testng’ to classpath

pom.xml文件会自动增加依赖:

2)@Test 需要导入包

import org.testng.annotations.Test;

3、运行结果



@Test是最基本的注解,用来把方法标记成测试用例

2.2 注解实战 BeforeMethod和AfterMethod

1、在测试方法之前运行的标签 @BeforeMethod

2、在测试方法之后运行的标签 @AfterMethod



3、多个测试方法执行

说明:如果有多个测试方法,在每一个测试方法之前和之后,BeforeMethod、AfterMethod都运行一次。

2.3 注解实战BeforeClass和AfterClass

1、在测试类之前运行的标签 @BeforeClass

2、在测试类之后运行的标签 @AfterClass



说明:BeforeClass 和 AfterClass在类中,只运行一次。

注册对象,静态方法,变量赋值等都可以用到。

2.4 注解实战:BeforeSuite和AfterSuit

1、BeforeSuite 在整个测试套件开始之前运行

2、AfterSuite 在整个测试套件结束之后运行



说明:BeforeSuit 和 AfterSuit 在类中,只运行一次。

  • 关注点在于执行顺序:
  • beforeSuite --> beforeClass --> beforeMethod --> 测试方法1 --> afterMethod --> beforeMethod --> 测试方法2 --> afterMethod --> afterClass --> afterSuite

2.5 Before/After注解总结

代码举例:

package com.course.test;
import org.testng.annotations.*; public class BasicAnnotation { @Test
public void testCase1(){
System.out.println("这是测试用例1");
} @Test
public void testCase2(){
System.out.println("这是测试用例2");
} @BeforeMethod
public void beforeMethod(){
System.out.println("beforeMethod在测试方法运行之前运行");
} @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在类运行之后运行");
}
}

结果:

beforeSuite在类运行之前运行
beforeClass在类运行之前运行
beforeMethod在测试方法运行之前运行
这是测试用例1
afterMethod在测试方法运行之后运行
beforeMethod在测试方法运行之前运行
这是测试用例2
afterMethod在测试方法运行之后运行
afterClass在类运行之后运行
afterSuite在类运行之后运行

三、套件测试

测试套件是用于测试软件程序的行为或一组行为的测试用例的集合。在TestNG中,我们无法定义一个套件,但它可以由一个XML文件表示,因为套件是执行的功能。它还允许灵活配置要运行的测试。 套件可以包含一个或多个测试,并由标记定义。

  • 是testng.xml的根标记。 它描述了一个测试套件,它又由几个部分组成。
  • 接收的所有定义的合法属性。

3.1 创建suite包

创建类:suiteConfig.java、LoginTest.java、PayTest.java

package com.course.test.suite;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest; // 测试套件之前需要运行的方法
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");
}
}
package com.course.test.suite;
import org.testng.annotations.Test; // 写测试类
public class LoginTest {
@Test
public void loginTaobao(){
System.out.println("淘宝登录成功!");
}
}
package com.course.test.suite;
import org.testng.annotations.Test; public class PayTest {
@Test
public void paySuccess(){
System.out.println("淘宝支付成功!");
}
}

3.2 在 resources中创建suite.xml

目录:src/main/resources/suite.xml

<?xml version="1.0" encoding="UTF-8" ?>

<suite name="test">
<test name="login">
<classes>
<class name="com.course.test.suite.SuiteConfig" />
<class name="com.course.test.suite.LoginTest"/>
</classes>
</test> <test name="pay">
<classes>
<class name="com.course.test.suite.SuiteConfig" />
<class name="com.course.test.suite.PayTest"/>
</classes>
</test>
</suite>
  • 说明:使用suite.xml文件,该文件用来管理测试用例,并与运行testNG。

    套件就是将所有测试类整理在一起,形成一套测试用例

    测试集是指测试模块,一般一个项目可以按照模块分几部分,即不同的test

    测试集下的所有测试类

    具体测试类,name 属性指定测试类的路径

    测试类下具体的测试方法,如果不写此标签,则默认包含测试类下的所有方法

3.3 运行suite.xml文件

运行结果:

说明:

测试套件test中,有2个测试模块login和pay。

login模块下的测试类是 SuiteConfig.java和LoginTest.java,执行该测试类下所有方法。

pay模块下的测试类是 SuiteConfig.java和PayTest.java,执行该测试类下所有方法。

由于suite.xml只有一个测试套件test,所以beforeSuite()/afterSuite()只运行一次。beforeTest()/afterTest()针对每个测试方法都执行一次。测试方法 loginTaobao()、paySuccess() 分别执行一次。

四、忽略测试

1、什么时忽略测试:本次测试执行不想执行该用例,或者编写的代码没有准备就绪,并且测试用例要测试该方法/代码是否成功或失败。

使用注释 @Test(enable=false) 禁用此测试用例,绕过该测试用例。

2、新建 IgnoreTest.java

package com.course.testng;
import org.testng.annotations.Test; public class IgnoreTest {
@Test
public void ignore1(){
System.out.println("ignore1 执行!");
} @Test(enabled = false)
public void ignore2(){
System.out.println("ignore2 执行!");
} @Test(enabled = true)
public void ignore3(){
System.out.println("ignore3 执行!");
}
}

执行结果:

ignore1 执行!
ignore3 执行!

说明: @Test(enabled = false) 的用例会被忽略

五、分组测试

分组测试允许你将方法调度到适当的部分,并执行复杂的测试方法分组。

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

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

使用 标记在testng.xml文件中指定分组。 它可以在或标签下找到。

标签中指定分组适用于其下的所有标签。

5.1 分组测试-方法分组测试

1、新建组groups,新建类 GroupsOnMethod.java

package com.course.testng.groups;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test; public class GroupsOnMethod {
@Test(groups = "server")
public void test1(){
System.out.println("这是服务端组的测试方法1111");
} @Test(groups = "server")
public void test2(){
System.out.println("这是服务端组的测试方法2222");
} @Test(groups = "client")
public void test3(){
System.out.println("这是客户端组的测试方法3333");
} @Test(groups = "client")
public void test4(){
System.out.println("这是客户端组的测试方法4444");
} @BeforeGroups("server")
public void beforeGroupsOnServer(){
System.out.println("这是服务端组运行之前运行的方法:beforeGroupsOnServer");
} @AfterGroups("server")
public void afterGroupsOnServer(){
System.out.println("这是服务端组运行之后运行的方法:afterGroupsOnServer");
} @BeforeGroups("client")
public void beforeGroupsOnClient(){
System.out.println("这是客户端组运行之前运行的方法:beforeGroupsOnClient");
} @AfterGroups("client")
public void afterGroupsOnClient(){
System.out.println("这是客户端组运行之后运行的方法:afterGroupsOnClient");
}
}

结果:

这是服务端组运行之前运行的方法:beforeGroupsOnServer
这是服务端组的测试方法1111
这是服务端组的测试方法2222
这是服务端组运行之后运行的方法:afterGroupsOnServer
这是客户端组运行之前运行的方法:beforeGroupsOnClient
这是客户端组的测试方法3333
这是客户端组的测试方法4444
这是客户端组运行之后运行的方法:afterGroupsOnClient

说明:

  • 方法test1()、test2() 属于分组server,beforeGroupsOnServer()在server组的方法执行之前运行,afterGroupsOnServer()在server组的方法执行之后运行;
  • 方法test3()、test4() 属于分组client,beforeGroupsOnClient()在server组的方法执行之前运行,afterGroupsOnClient()在client组的方法执行之后运行。

5.2 分组测试中-类分组测试

1、新建组:groups,新建类 GroupsOnClass1.java、 GroupsOnClass2.java、GroupsOnClass3.java

其中 GroupsOnClass1.java和GroupsOnClass2.java是一个分组stu;GroupsOnClass3.java是一个分组teacher

package com.course.testng.groups;
import org.testng.annotations.Test; @Test(groups = "stu")
public class GroupsOnClass1 {
public void stu1(){
System.out.println("GroupsOnClass1 中的stu1111运行");
} public void stu2(){
System.out.println("GroupsOnClass1 中的stu2222运行");
}
}
package com.course.testng.groups;
import org.testng.annotations.Test;
@Test(groups = "stu")
public class GroupsOnClass2 {
public void stu1(){
System.out.println("GroupsOnClass2 中的stu1111运行");
} public void stu2(){
System.out.println("GroupsOnClass2 中的stu2222运行");
}
}
package com.course.testng.groups;
import org.testng.annotations.Test; @Test(groups = "teacher")
public class GroupsOnClass3 {
public void teacher1(){
System.out.println("GroupsOnClass3 中的teacher1111运行");
} public void teacher2(){
System.out.println("GroupsOnClass3 中的teacher2222运行");
}
}

2、新建 groupsOnClass.xml

<?xml version="1.0" encoding="UTF-8" ?>

<suite name="suitename">
<test name="runAll">
<classes>
<class name="com.course.testng.groups.GroupsOnClass1"/>
<class name="com.course.testng.groups.GroupsOnClass2"/>
<class name="com.course.testng.groups.GroupsOnClass3"/>
</classes>
</test> <test name="OnlyRunStu">
<groups>
<run>
<include name="stu"/>
</run>
</groups>
<classes>
<class name="com.course.testng.groups.GroupsOnClass1"/>
<class name="com.course.testng.groups.GroupsOnClass2"/>
<class name="com.course.testng.groups.GroupsOnClass3"/>
</classes>
</test>
</suite>

说明:

测试套件suitename中有2个测试模块,runAllOnlyRunStu

runAll模块下的测试类是 GroupsOnClass1.java、GroupsOnClass2.java、GroupsOnClass3.java,执行该测试类下所有方法。

OnlyRunStu模块下的测试类是 GroupsOnClass1.java、GroupsOnClass2.java、GroupsOnClass3.java,只执行分组为 stu 的类下的方法。

3、运行xml文件,结果:

GroupsOnClass1 中的stu1111运行
GroupsOnClass1 中的stu2222运行
GroupsOnClass2 中的stu1111运行
GroupsOnClass2 中的stu2222运行
GroupsOnClass3 中的teacher1111运行
GroupsOnClass3 中的teacher2222运行
GroupsOnClass1 中的stu1111运行
GroupsOnClass1 中的stu2222运行
GroupsOnClass2 中的stu1111运行
GroupsOnClass2 中的stu2222运行

六、异常测试

1、什么时候会用到异常测试

在我们期望结果为某个异常时,比如:我们传入了某些不合法参数,程序抛出了异常。也就是说预期结果就是该异常。

2、运行时异常和非运行时异常



3、举例

1)新建 ExpectedException.java

package com.course.testng;
import org.testng.annotations.Test; 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();
}
}

2)结果:两个用例,pass一个,fail一个

七、依赖测试

7.1 依赖测试

有时我们可能需要以特定顺序调用测试用例中的方法,或者希望在方法之间共享一些数据和状态。

TestNG支持这种依赖关系,因为它支持在测试方法之间显式依赖的声明。

TestNG允许指定依赖关系:

  • @Test注解中使用属性 dependsOnMethods
  • @Test注解中使用属性 dependsOnGroups

    在TestNG中,我们使用dependOnMethodsdependsOnGroups 来实现依赖测试。 如果依赖方法失败,则将跳过所有后续测试方法。

    举例:test1() 依赖于 test2()

7.2 dependsOnMethods

1、如果test1()成功,则执行test2()

新建 DependTest.java

package com.course.testng;
import org.testng.annotations.Test; public class DependTest {
@Test
public void test1(){
System.out.println("test1 run");
} @Test(dependsOnMethods = {"test1"})
public void test2(){
System.out.println("test2 run");
}
}

只运行 test2() ,结果:

2、如果test1()失败,则跳过test2()

修改test1(),使得其运行失败;test2 依赖的方法失败,自己也不会运行成功

package com.course.testng;
import org.testng.annotations.Test; public class DependTest {
@Test
public void test1(){
System.out.println("test1 run");
throw new RuntimeException();
} @Test(dependsOnMethods = {"test1"})
public void test2(){
System.out.println("test2 run");
}
}

只运行 test2() ,结果:用例执行失败一个,忽略一个

7.3 dependsOnGroups

1、创建java文件:TestServer.java

package com.course.testng.depend;

import org.testng.annotations.Test;

@Test(groups = "deploy")
public class TestServer {
@Test
public void deployServer(){
System.out.println("运行服务");
} @Test(dependsOnMethods = "deployServer")
public void deployBackUpServer(){
System.out.println("如果方法deployServer()成功则运行");
}
}

2、创建 TestDatabase.java

package com.course.testng.depend;

import org.testng.annotations.Test;

public class TestDatabase {

    @Test(groups = "db",dependsOnGroups = "deploy")
public void initDB(){
System.out.println("这是initDB方法");
} @Test(dependsOnMethods = {"initDB"},groups = "db")
public void testConnection(){
System.out.println("这是testConnection方法");
}
}

3、创建TestApp.java

package com.course.testng.depend;

import org.testng.annotations.Test;

public class TestApp {
@Test(dependsOnGroups = {"deploy","db"})
public void method1(){
System.out.println("这是方法1");
} @Test(dependsOnMethods = "method1")
public void method2(){
System.out.println("这是方法2");
}
}

4、在resource文件下创建depend.xml

<?xml version="1.0" encoding="UTF-8" ?>
<suite name="TestDependency">
<test name="TestCase1">
<classes>
<class name="com.course.testng.depend.TestApp">
</class>
<class name="com.course.testng.depend.TestDatabase">
</class>
<class name="com.course.testng.depend.TestServer">
</class>
</classes>
</test>
</suite>

5、执行结果:

八、参数化测试

8.1 参数化测试-xml文件参数化

1、如何通过外部或内部传递参数

2、创建包 parameter,创建类 ParameterTest.java

package com.course.testng.parameter;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test; public class ParameterTest {
@Test
@Parameters({"name","age"})
public void paramTest1(String name,String age){
System.out.println("name = "+name+"; age = "+age);
}
}

3、创建Parameter.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="All Test Suite">
<test verbose="2" preserve-order="true" name="D:/testngDemo">
<parameter name="name" value="tom"/>
<parameter name="age" value="18"/>
<classes>
<class name="com.course.testng.parameter.ParameterTest"/>
</classes>
</test>
</suite>

4、运行xml文件,运行结果

8.2 参数化测试-DataProvider

1、第一种参数化方式其实比较鸡肋,第二种方式才是TestNG参数化的灵魂,用到了@DataProvider,它会返回一个二维数组:

package org.example;

import org.testng.Assert;
import org.testng.annotations.*; public class AppTest {
// 定义一个数据提供器,叫test01,返回二维数组
@DataProvider(name = "test01")
public Object[][] data() {
return new Object[][] {
{"tom", 18},
{"jack", 20}
};
} // 引用这个数据提供器
@Test(dataProvider = "test01")
public void test01(String name, int age){
System.out.println(name + ": " + age);
}
}

结果:

切记:

  • @DataProvider用于生产数据,name是唯一标识。
  • 在@Test中通过dataProvider属性指定name。
  • 测试方法的入参跟数组中元素一一对应。

2、Iterator

@DataProvide的返回值(参数类型)除了已经提到的Object[][],还可以时Iterator,它不会一次性生成所有数据,而是每调用一次生成一次,节约内存。

package com.course.testng.parameter;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.Iterator; public class ParameterTest3 {
@DataProvider(name = "test01")
public Iterator<Object[]> data(){
Object[][] myObject = new Object[][]{
{"tom",18},
{"jack",20}
};
return Arrays.asList(myObject).iterator();
}
@Test(dataProvider = "test01")
public void test01(String name,int age){
System.out.println(name+":"+age);
}
}

结果:

总结:DataProvider支持数组、迭代器

3、通过方法名传递参数

package com.course.testng.parameter;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.lang.reflect.Method; public class ParameterTest4 {
@Test(dataProvider = "methodData")
public void test1(String name,int age){
System.out.println("test1方法 name="+name+";age="+age);
} @Test(dataProvider = "methodData")
public void test2(String name,int age){
System.out.println("test2方法 name="+name+";age="+age);
} @DataProvider(name = "methodData")
public Object[][] methodDataTest(Method method){
Object[][] result = null;
if (method.getName().equals("test1")){
result = new Object[][]{
{"hqq1",18},
{"hqq2",19}
};
}else if (method.getName().equals("test2")){
result = new Object[][]{
{"guihua1",20},
{"guihua2",22}
};
}
return result;
}
}

结果:

test1方法 name=hqq1;age=18

test1方法 name=hqq2;age=19

test2方法 name=guihua1;age=20

test2方法 name=guihua2;age=22

九、多线程测试

实现testng多线程的两种方式:

  • 注解实现
  • xml实现

9.1 注解实现

package com.course.testng.thread;
import org.testng.annotations.Test; public class MultiThreadByAnnotation {
/**
* threadPoolSize 为线程池内可使用的线程数
* 使用threadPoolSize个线程,将test方法执行invocationCount次
* timeOut配置的是每次执行该测试方法所耗费时间的阈值,超过阈值则测试失败
*/
@Test(invocationCount = 10,threadPoolSize = 3,timeOut = 1000)
public void test(){
System.out.println("hello");
System.out.println("Thread Id:"+Thread.currentThread().getId());
}
}

执行结果:使用了3个线程,将测试方法test执行了10次。

hello
hello
hello
Thread Id:12
Thread Id:13
Thread Id:14
hello
hello
Thread Id:12
hello
Thread Id:14
Thread Id:13
hello
Thread Id:12
Thread Id:14
hello
hello
Thread Id:13
hello
Thread Id:12

9.2 xml实现test、class、method级别的并发

1)需要在 testng.xml中suite tag下设置

<suite name="Testng Parallel Test" parallel="tests" thread-count="5">
<suite name="Testng Parallel Test" parallel="classes" thread-count="5">
<suite name="Testng Parallel Test" parallel="methods" thread-count="5">

他们的共同点都是最多起5个线程去同时执行不同的用例。thread-count 代表最大并发线程数。

他们的区别如下:

  • method级别:所有用例都可以在不同的线程去执行
  • class级别:不同class tag 下的用例,在不同的线程执行;相同class tag下的用例只能在同一个线程中执行
  • tests级别:不同test tag 下的用例,在不同的线程执行;相同test tag下的用例只能在同一个线程中执行

    意义:可以将非线程安全的测试类或group统一放到一个test中,这样在并发的同时又可以保证这些类里面的用例是单线程执行。也可以根据需要设定class级别的并发,让同一个测试类里的用例在同一个线程中执行。

    补充:xml文件配置这种方式不能指定线程池,只有方法上才可以指定线程池。

2)举例实现1:methods

package com.course.testng.thread;
import org.testng.annotations.Test; public class MultiThreadByXml {
@Test
public void test1(){
System.out.println("test1---Thread Id : " + Thread.currentThread().getId());
} @Test
public void test2(){
System.out.println("test2---Thread Id : " + Thread.currentThread().getId());
} @Test
public void test3(){
System.out.println("test3---Thread Id : " + Thread.currentThread().getId());
}
}

新增multiThread.xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <!--parallel="methods",表示多线程级别为方法级别-->
<!--thread-count="2",表示线程数为2-->
<suite name="thread" parallel="methods" thread-count="2">
<test name="demo1">
<classes>
<class name="com.course.testng.thread.MultiThreadByXml"/>
</classes>
</test>
</suite>

执行xml文件:



说明:所有用例都可以在不同的线程去执行。

3)举例实现2:classes

新增 MultiThreadByXml.java、MultiThreadByXml2.java、MultiThreadByXml3.java

package com.course.testng.thread;
import org.testng.annotations.Test; public class MultiThreadByXml {
@Test
public void test1(){
System.out.println("test1---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test2(){
System.out.println("test2---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test3(){
System.out.println("test3---Thread Id : " + Thread.currentThread().getId());
}
}
package com.course.testng.thread;
import org.testng.annotations.Test; public class MultiThreadByXml2 {
@Test
public void test1(){
System.out.println("test21---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test2(){
System.out.println("test22---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test3(){
System.out.println("test23---Thread Id : " + Thread.currentThread().getId());
}
}
package com.course.testng.thread;
import org.testng.annotations.Test; public class MultiThreadByXml3 {
@Test
public void test1(){
System.out.println("test31---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test2(){
System.out.println("test32---Thread Id : " + Thread.currentThread().getId());
}
@Test
public void test3(){
System.out.println("test33---Thread Id : " + Thread.currentThread().getId());
}
}

说明:改xml的配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <!--parallel="classes",表示多线程级别为class-->
<!--thread-count="2",表示线程数为2-->
<suite name="thread" parallel="classes" thread-count="2">
<test name="demo1">
<classes>
<class name="com.course.testng.thread.MultiThreadByXml"></class>
<class name="com.course.testng.thread.MultiThreadByXml2"></class>
</classes>
</test> <test name="demo2">
<classes>
<class name="com.course.testng.thread.MultiThreadByXml3"></class>
</classes>
</test>
</suite>

结果:



说明:不同class tag下的用例可以在不同线程中执行;同一个class tag标签下的用例只能在同一个线程中执行。

4)举例实现3:tests

代码同上一个例子,只改xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!--parallel="tests",表示多线程级别为test级别-->
<!--thread-count="2",表示线程数为2-->
<suite name="thread" parallel="tests" thread-count="2">
<test name="demo1">
<classes>
<class name="com.course.testng.thread.MultiThreadByXml"></class>
</classes>
</test> <test name="demo2">
<classes>
<class name="com.course.testng.thread.MultiThreadByXml2"></class>
</classes>
</test>
</suite>



说明:不同test tag下的用例可以在不同的线程执行,相同test tag下的用例只能在同一个线程中执行。

9.3 多线程总结

参考文档:https://testerhome.com/topics/7895

9.3.1 多线程并发执行

必须要指出的是,通过多线程执行用例时虽然可以大大提升用例的执行效率,但是我们在设计用例时也要考虑到这些用例是否适合并发执行,以及要注意多线程方式的通病:线程安全与共享变量的问题。建议是在测试代码中,尽可能地避免使用共享变量。如果真的用到了,要慎用 synchronized 关键字来对共享变量进行加锁同步。否则,难免你的用例执行时可能会出现不稳定的情景(经常听到有人提到用例执行地不稳定,有时 100% 通过,有时只有 90% 通过,猜测可能有一部分原因也是这个导致的)。

9.3.2 不同级别的并发

通常,在 TestNG 的执行中,测试的级别由上至下可以分为suite -> test -> class -> method,箭头的左边元素跟右边元素的关系是一对多的包含关系。

这里的 test 指的是 testng.xml 中的 test tag,而不是测试类里的一个@Test。测试类里的一个@Test实际上对应这里的 method。所以我们在使用@BeforeSuite、@BeforeTest、@BeforeClass、@BeforeMethod这些标签的时候,它们的实际执行顺序也是按照这个级别来的。

1)suite

一般情况下,一个 testng.xml 只包含一个 suite。如果想起多个线程执行不同的 suite,官方给出的方法是:通过命令行的方式来指定线程池的容量。

java org.testng.TestNG -suitethreadpoolsize 3 testng1.xml testng2.xml testng3.xml

即可通过三个线程来分别执行 testng1.xml、testng2.xml、testng3.xml。

实际上这种情况在实际中应用地并不多见,我们的测试用例往往放在一个 suite 中,如果真需要执行不同的 suite,往往也是在不同的环境中去执行,届时也自然而然会做一些其他的配置(如环境变量)更改,会有不同的进程去执行。因此这种方式不多赘述。

2)test, class, method

test,class,method 级别的并发,可以通过在 testng.xml 中的 suite tag 下设置。

  • tests 级别:不同 test tag 下的用例可以在不同的线程执行,相同 test tag 下的用例只能在同一个线程中执行。
  • classs 级别:不同 class tag 下的用例可以在不同的线程执行,相同 class tag 下的用例只能在同一个线程中执行。
  • methods 级别:所有用例都可以在不同的线程去执行。

十、超时测试

10.1 套件级别的超时测试示例

public class TimeoutSuite
{
@Test
public void timeTestOne() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Time test method one");
} @Test
public void timeTestTwo() throws InterruptedException {
Thread.sleep(400);
System.out.println("Time test method two");
}
}

添加对应的testng.xml文件

<suite name="Time test Suite" time-out="500" verbose="1" >
<test name="Timeout Test" >
<classes>
<class name="com.howtodoinjava.test.TimeoutSuite"/>
</classes>
</test>
</suite>

运行结果:

[TestNG] Running: C:\somepath\TestNGExamples\testng.xml

Time test method two

===============================================
Time test Suite
Total tests run: 2, Failures: 1, Skips: 0
===============================================

从测试结果中可以看出,只有timeTestTwo()被执行,因为它的执行时间少于testng.xml文件中定义的超时时间。

timeTestOne()执行被取消,因为执行完成所需的时间,超过配置的超时时间。

10.2 方法级别的超时测试

public class TimeoutSuite
{
@Test(timeOut=500)
public void timeTestOne() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Time test method one");
} @Test(timeOut=500)
public void timeTestTwo() throws InterruptedException {
Thread.sleep(400);
System.out.println("Time test method two");
}
}

执行结果:

[[TestNG] Running: C:\Users\somepath\testng-customsuite.xml
Time test method two
PASSED: timeTestTwo
FAILED: timeTestOne org.testng.internal.thread.ThreadTimeoutException: Method org.testng.internal.TestNGMethod.timeTestOne() didn't finish within the time-out 500 ===============================================
Default test
Tests run: 2, Failures: 1, Skips: 0
===============================================

说明:@Test(timeOut=xxx) 指定超时时间,单位为毫秒。

十一、软断言和硬断言

TestNG提供两种断言方式,分别是硬断言或或者软断言,类似于pytest里面的断言和多重断言。

硬断言时Assert直接调用静态方法,软断言需要实例化,才能调用断言方法。

硬断言(Assert):当一个测试用例中存在多个断言,当有一个断言失败时,则会抛出异常,不再执行该用例中后续的断言;

软断言(SoftAssert):当一个测试用例中存在多个断言,当有一个断言失败时,会执行后续的断言。

11.1 硬断言

Assert.assertEquals(actual,expected)            查看两个对象是否相等;类似于字符串比较实用equals()方法
Assert.assertNotEquals(actual,expected) 查看两个对象是否不相等;
Assert.assertNull(object) 查看对象是否为空;
Assert.assertNotNull(object) 查看对象是否不为空;
Assert.assertSame(actual,expected) 查看两个对象的引用是否相等,类似于使用“==”比较两个对象
Assert.assertNotSame(actual,expected) 查看两个对象的引用是否不相等,类似于使用“!=”比较两个对象
Assert.assertTrue(condition) 判断条件是否为true
Assert.assertFalse(condition) 判断条件是否为false
assertArrayEquals(actual,expected) 判断两个数组是否相等。
Assert.fail(); 让测试用例失败

11.2 软断言

SoftAssert的特点:

  1. 如果一个断言失败,会继续执行这个断言下的其他语句或者断言
  2. 也就是一个用例有多个断言,失败了其中一个,不影响其他断言的运行
  3. 千万不要忘记在该用例的最后一个断言后面调用assertAll(),否则断言不执行

总结

在实际的测试工作中,一个测试用例中往往包含多个断言,更适合用软断言,一个断言失败,不影响其他的断言。可能会有疑问,不管是软断言还是硬断言,当用例失败时,都需要我们人为的去检查错误,认为两者是没有区别的。

其实还是有区别的,硬断言报错时只知道当前断言异常,并不知道后续断言是否成功。只调整了当前的断言,之后再次运行脚本,后续断言还有出错的概率,需要反复运行确定断言的正确性,很是耽误测试时间,影响工作效率。而软断言呢?可以知道所有失败的断言,可以统一进行调整。

测试框架TestNG学习笔记的更多相关文章

  1. Mina框架的学习笔记——Android客户端的实现

    Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络 ...

  2. go微服务框架kratos学习笔记五(kratos 配置中心 paladin config sdk [断剑重铸之日,骑士归来之时])

    目录 go微服务框架kratos学习笔记五(kratos 配置中心 paladin config sdk [断剑重铸之日,骑士归来之时]) 静态配置 flag注入 在线热加载配置 远程配置中心 go微 ...

  3. # go微服务框架kratos学习笔记六(kratos 服务发现 discovery)

    目录 go微服务框架kratos学习笔记六(kratos 服务发现 discovery) http api register 服务注册 fetch 获取实例 fetchs 批量获取实例 polls 批 ...

  4. go微服务框架kratos学习笔记七(kratos warden 负载均衡 balancer)

    目录 go微服务框架kratos学习笔记七(kratos warden 负载均衡 balancer) demo demo server demo client 池 dao service p2c ro ...

  5. go微服务框架kratos学习笔记八 (kratos的依赖注入)

    目录 go微服务框架kratos学习笔记八(kratos的依赖注入) 什么是依赖注入 google wire kratos中的wire Providers injector(注入器) Binding ...

  6. DBFlow框架的学习笔记之入门

    什么是DBFlow? dbflow是一款android高性的ORM数据库.可以使用在进行项目中有关数据库的操作.github下载源码 1.环境配置 先导入 apt plugin库到你的classpat ...

  7. go微服务框架kratos学习笔记十(熔断器)

    目录 go微服务框架kratos学习笔记十(熔断器) 什么是熔断 熔断器逻辑 kratos Breaker kratos 熔断逻辑 kratos熔断器使用说明 bladmaster client br ...

  8. go微服务框架kratos学习笔记三(构建单独的http或者grpc demo项目)

    go微服务框架kratos学习笔记三(构建单独的http或者grpc demo项目) 前面两篇跑通了demo项目,和大概了解了kratos demo整体结构,本篇分别构建一个http和一个grpc微服 ...

  9. go微服务框架kratos学习笔记二(kratos demo 结构)

    目录 api cmd configs dao di model server service 上篇文章go微服务框架kratos学习笔记一(kratos demo)跑了kratos demo 本章来看 ...

  10. jfinal框架教程-学习笔记

    jfinal框架教程-学习笔记 JFinal  是基于 Java  语言的极速  WEB  + ORM  开发框架,其核心设计目标是开发迅速.代码量少.学习简单.功能强大.轻量级.易扩展.Restfu ...

随机推荐

  1. What is TLS ?

    TLS intrduction TLS是一种保障数据传输安全的一种技术(方案),它在传输层工作. 想知道TLS和SSL的关系,先看看他们的历史: 人们遇到数据传输安全问题 1994 SSL 1.0 1 ...

  2. 设置启动WebSocket并配置程序池

    开启WebSocket服务 1.导航到"控制面板" > "程序" > "程序和功能" > "启用或禁用 Wind ...

  3. ES13 中11个令人惊叹的 JavaScript 新特性

    前言 与许多其他编程语言一样,JavaScript 也在不断发展.每年,该语言都会通过新功能变得更加强大,使开发人员能够编写更具表现力和简洁的代码. 小编今天就为大家介绍ES13中添加的最新功能,并查 ...

  4. python入门基础(14)--类的属性、成员方法、静态方法以及继承、重载

    上一篇提到过类的属性,但没有详细介绍,本篇详细介绍一下类的属性 一 .类的属性 方法是用来操作数据的,而属性则是建模必不的内容,而且操作的数据,大多数是属性,比如游戏中的某个boss类,它的生命值就是 ...

  5. Java新特性中的Preview功能如何运行和调试

    在每个Java新版本发布的特性中,都会包含一些Preview(预览)功能,这些功能主要用来给开发者体验并收集建议.所以,Preview阶段的功能并不是默认开启的. 如果想体验某个Java版本中的Pre ...

  6. python第一章 学习笔记 计算机基础知识 Sublime Text 3

    ## 计算机是什么 在现实生活中,越来越无法离开计算机了 电脑.笔记本.手机.游戏机.汽车导航.智能电视 ... 计算机就是一个用来计算的机器! 目前来讲,计算机只能根据人类的指令来完成各种操作,人让 ...

  7. linux日常运维(一)

    - 进程管理ps.top - 任务计划crontab 进程管理 概念:进程是在自身的虚拟地址空间运行的一个单独的程序. 进程和程序区别:程序只是一个静态的命令集合,不占系统的运行资源:而进程是一个随时 ...

  8. 如何用ppt打印9张一面,并且去除边距?

    如何用ppt打印9张一面,并且去除边距?      方法其实很简单,答主不要在ppt软件的打印选项里设置[每页打印9张幻灯片],而是使用默认的[每页打印1张幻灯片]. 然后去[打印机属性]里设置,我是 ...

  9. 第五周单元测验题英语教学与互联网 mooc

    第五周单元测验题 返回 本次得分为:16.00/20.00, 本次测试的提交时间为:2020-08-30, 如果你认为本次测试成绩不理想,你可以选择 再做一次 . 1 单选(2分) 从评价的主体来看, ...

  10. C#归并排序算法

    前言 归并排序是一种常见的排序算法,它采用分治法的思想,在排序过程中不断将待排序序列分割成更小的子序列,直到每个子序列中只剩下一个元素,然后将这些子序列两两合并并排序,最终得到一个有序的序列. 归并排 ...