测试框架TestNG学习笔记
一、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个测试模块,runAll和OnlyRunStu。
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中,我们使用dependOnMethods和dependsOnGroups来实现依赖测试。 如果依赖方法失败,则将跳过所有后续测试方法。
举例: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的特点:
- 如果一个断言失败,会继续执行这个断言下的其他语句或者断言
- 也就是一个用例有多个断言,失败了其中一个,不影响其他断言的运行
- 千万不要忘记在该用例的最后一个断言后面调用assertAll(),否则断言不执行
总结
在实际的测试工作中,一个测试用例中往往包含多个断言,更适合用软断言,一个断言失败,不影响其他的断言。可能会有疑问,不管是软断言还是硬断言,当用例失败时,都需要我们人为的去检查错误,认为两者是没有区别的。
其实还是有区别的,硬断言报错时只知道当前断言异常,并不知道后续断言是否成功。只调整了当前的断言,之后再次运行脚本,后续断言还有出错的概率,需要反复运行确定断言的正确性,很是耽误测试时间,影响工作效率。而软断言呢?可以知道所有失败的断言,可以统一进行调整。
测试框架TestNG学习笔记的更多相关文章
- Mina框架的学习笔记——Android客户端的实现
Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络 ...
- go微服务框架kratos学习笔记五(kratos 配置中心 paladin config sdk [断剑重铸之日,骑士归来之时])
目录 go微服务框架kratos学习笔记五(kratos 配置中心 paladin config sdk [断剑重铸之日,骑士归来之时]) 静态配置 flag注入 在线热加载配置 远程配置中心 go微 ...
- # go微服务框架kratos学习笔记六(kratos 服务发现 discovery)
目录 go微服务框架kratos学习笔记六(kratos 服务发现 discovery) http api register 服务注册 fetch 获取实例 fetchs 批量获取实例 polls 批 ...
- go微服务框架kratos学习笔记七(kratos warden 负载均衡 balancer)
目录 go微服务框架kratos学习笔记七(kratos warden 负载均衡 balancer) demo demo server demo client 池 dao service p2c ro ...
- go微服务框架kratos学习笔记八 (kratos的依赖注入)
目录 go微服务框架kratos学习笔记八(kratos的依赖注入) 什么是依赖注入 google wire kratos中的wire Providers injector(注入器) Binding ...
- DBFlow框架的学习笔记之入门
什么是DBFlow? dbflow是一款android高性的ORM数据库.可以使用在进行项目中有关数据库的操作.github下载源码 1.环境配置 先导入 apt plugin库到你的classpat ...
- go微服务框架kratos学习笔记十(熔断器)
目录 go微服务框架kratos学习笔记十(熔断器) 什么是熔断 熔断器逻辑 kratos Breaker kratos 熔断逻辑 kratos熔断器使用说明 bladmaster client br ...
- go微服务框架kratos学习笔记三(构建单独的http或者grpc demo项目)
go微服务框架kratos学习笔记三(构建单独的http或者grpc demo项目) 前面两篇跑通了demo项目,和大概了解了kratos demo整体结构,本篇分别构建一个http和一个grpc微服 ...
- go微服务框架kratos学习笔记二(kratos demo 结构)
目录 api cmd configs dao di model server service 上篇文章go微服务框架kratos学习笔记一(kratos demo)跑了kratos demo 本章来看 ...
- jfinal框架教程-学习笔记
jfinal框架教程-学习笔记 JFinal 是基于 Java 语言的极速 WEB + ORM 开发框架,其核心设计目标是开发迅速.代码量少.学习简单.功能强大.轻量级.易扩展.Restfu ...
随机推荐
- 关于API数据接口获取商品的数据的说明
获取商品数据已经成为许多应用程序的重要组成部分.为了实现这一目标,许多公司和技术开发者使用API数据接口来获取相关数据.本文将详细介绍如何使用API数据接口获取商品数据,并使用Python作为编程 ...
- 国内镜像安装Python解释器及扩展包
一.下载Python解释器 1.下载地址 官网(下载速度很慢):Welcome to Python.org 淘宝镜像(推荐):CNPM Binaries Mirror (npmmirror.com) ...
- PanGu-Coder2:从排序中学习,激发大模型潜力
本文分享自华为云社区<PanGu-Coder2:从排序中学习,激发大模型潜力>,作者: 华为云软件分析Lab . 2022年7月,华为云PaaS技术创新Lab联合华为诺亚方舟语音语义实验室 ...
- 如何解决IOS 15提示“此App的开发者需要更新APP以在此IOS版本上正常工作”, 无法打开安装的APP的问题
在苹果手机最新的IOS 15 beta的系统上安装自签名或者企业签名的APP时,可能会遇到如下的错误提示: 此App的开发者需要更新APP以在此IOS版本上正常工作 The developer of ...
- 小知识:PPT的幻灯片放映设置
最近给某客户讲课时,碰到了幻灯片自动翻页的情况,发现是因为之前做过粗略的计时演练,有些片子就快速过了. 问题现象: 结果导致放映时也出现了某些片子快速被自动翻页. 解决方案: 设置成手动推进幻灯片的方 ...
- 创建第一个C语言文件
创建第一个C语言文件 新建=>项目=>空项目 创建.c文件 我们学的是C语言,c++就不写了 调整字体 快捷键:Ctlr + 鼠标滚轮 通过工具调整 工具库与main()函数 打开一个工具 ...
- Solution Set -「CF 1539」
我是傻逼. 「CF 1539A」Contest Start Link. 答案是 \(\sum_{i=1}^{n-1}\min\{i,\lfloor\frac{t}{x}\rfloor\}\),等差数列 ...
- mpi转以太网连接300PLC实现以太网通信配置方法
西门子S7300PLC连接MPI-ETH-XD1.0实现以太网通信配置方法 产品简介 兴达易控MPI-ETH-XD1.0用于西门子S7-200/SMART S7-200/S7-300/S7-400/西 ...
- Java 21 新特性:虚拟线程(Virtual Threads)
在Java 21中,引入了虚拟线程(Virtual Threads)来简化和增强并发性,这使得在Java中编程并发程序更容易.更高效. 虚拟线程,也称为"用户模式线程(user-mode t ...
- Go结构体深度探索:从基础到应用
在Go语言中,结构体是核心的数据组织工具,提供了灵活的手段来处理复杂数据.本文深入探讨了结构体的定义.类型.字面量表示和使用方法,旨在为读者呈现Go结构体的全面视角.通过结构体,开发者可以实现更加模块 ...