测试框架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 ...
随机推荐
- What is TLS ?
TLS intrduction TLS是一种保障数据传输安全的一种技术(方案),它在传输层工作. 想知道TLS和SSL的关系,先看看他们的历史: 人们遇到数据传输安全问题 1994 SSL 1.0 1 ...
- 设置启动WebSocket并配置程序池
开启WebSocket服务 1.导航到"控制面板" > "程序" > "程序和功能" > "启用或禁用 Wind ...
- ES13 中11个令人惊叹的 JavaScript 新特性
前言 与许多其他编程语言一样,JavaScript 也在不断发展.每年,该语言都会通过新功能变得更加强大,使开发人员能够编写更具表现力和简洁的代码. 小编今天就为大家介绍ES13中添加的最新功能,并查 ...
- python入门基础(14)--类的属性、成员方法、静态方法以及继承、重载
上一篇提到过类的属性,但没有详细介绍,本篇详细介绍一下类的属性 一 .类的属性 方法是用来操作数据的,而属性则是建模必不的内容,而且操作的数据,大多数是属性,比如游戏中的某个boss类,它的生命值就是 ...
- Java新特性中的Preview功能如何运行和调试
在每个Java新版本发布的特性中,都会包含一些Preview(预览)功能,这些功能主要用来给开发者体验并收集建议.所以,Preview阶段的功能并不是默认开启的. 如果想体验某个Java版本中的Pre ...
- python第一章 学习笔记 计算机基础知识 Sublime Text 3
## 计算机是什么 在现实生活中,越来越无法离开计算机了 电脑.笔记本.手机.游戏机.汽车导航.智能电视 ... 计算机就是一个用来计算的机器! 目前来讲,计算机只能根据人类的指令来完成各种操作,人让 ...
- linux日常运维(一)
- 进程管理ps.top - 任务计划crontab 进程管理 概念:进程是在自身的虚拟地址空间运行的一个单独的程序. 进程和程序区别:程序只是一个静态的命令集合,不占系统的运行资源:而进程是一个随时 ...
- 如何用ppt打印9张一面,并且去除边距?
如何用ppt打印9张一面,并且去除边距? 方法其实很简单,答主不要在ppt软件的打印选项里设置[每页打印9张幻灯片],而是使用默认的[每页打印1张幻灯片]. 然后去[打印机属性]里设置,我是 ...
- 第五周单元测验题英语教学与互联网 mooc
第五周单元测验题 返回 本次得分为:16.00/20.00, 本次测试的提交时间为:2020-08-30, 如果你认为本次测试成绩不理想,你可以选择 再做一次 . 1 单选(2分) 从评价的主体来看, ...
- C#归并排序算法
前言 归并排序是一种常见的排序算法,它采用分治法的思想,在排序过程中不断将待排序序列分割成更小的子序列,直到每个子序列中只剩下一个元素,然后将这些子序列两两合并并排序,最终得到一个有序的序列. 归并排 ...