TestNG框架
- 1.什么是TestNG
- 2.注解使用与实操(PC端)
- 2.1 基本测试@Test和@BeforeMethod、@AfterMethod、@BeforeClass和@AfterClass
- 2.2 套件测试
- 2.2.1 @BeforeSuite、@AfterSuite、@BeforeTest和@AfterTest注解方法
- 2.2.2 忽略测试@Test(enable=false)
- 2.3 分组测试
- 2.3.1 方法分组 @Test(groups="xxx")、@BeforeGroups、@AfterGroups
- 2.3.2 测试类分组 @Test(groups="xxx")
- 2.4 异常测试@Test(exceptedExceptions = XXXException.class)
- 2.5 依赖测试@Test(dependsOnMethods = {"funtion name"})
- 2.6 参数化测试
- 2.6.1 参数化测试1:@Parameters,在xml中传递参数
- 2.6.2 参数化测试2:@Test(dataProvider = "name")+@DataProvider
- 2.7 多线程测试
- 2.7.1 多线程测试注解实现 @Test(invocationCount=10,threadPoolSize=4)
- 2.7.2 多线程测试xml实现-parallel(methods|tests|classes)+thread-count
1.什么是TestNG
TestNG是JAVA的一个单元测试框架,类似于JUnit,是一种注解式的编程方式。和Junit主要的区别和适合测试工程师使用的原因:
- TestNG是比Junit涵盖功能更全面的测试框架,具有参数化和分组的特性,可以做数据驱动
- Junit更适合隔离性比较强的单元测试
- TestNG被设计应与覆盖所有的测试,单元、功能、端到端、集成测试等
- TestNG依赖测试时对于依赖方法失败后的用例标记为跳过而不是像Junit标记为失败,减少失败原因排查成本
- TestNG可以针对失败用例回归测试,增加测试针对性和效率,而Junit需要将所有测试用例重新执行
- TestNG更适合测试工程师需要的大范围的复杂的继集成测试
TestNG官方说明:https://testng.org/doc/documentation-main.htmlIBM Developer:https://www.ibm.com/developerworks/…
2.注解使用与实操(PC端)
测试IDE :AndroidStudio
测试框架:testNG
被测代码位置:
被测代码:Calculator.java
package yuyang.example.hellowng;
public class Calculator {
public int myAdd(int a, int b){return a+b;}
public int mySub(int a, int b){return a-b;}
}
将光标移动到被测类名称上,按键盘Ctrl+Shift+T
出现下图:
点击Creat New Test
:
2.1 基本测试@Test和@BeforeMethod、@AfterMethod、@BeforeClass和@AfterClass
点击Creat New Test
后,会在默认包目录下生成测试代码。
- 测试代码位置:
- 测试代码:
package yuyang.example.hellowng;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterClass;
import static org.testng.Assert.*;
public class CalculatorTest {
Calculator calculator = new Calculator();
@BeforeClass
public void beforeClass(){
System.out.println("@BeforeClass在测试类运行之前运行:");
}
@BeforeMethod
public void setUp() {
System.out.println("执行每个用例前执行setUp():");
}
@AfterMethod
public void tearDown() {
System.out.println("执行每个用例后执行tearDown():");
}
@Test
public void testMyAdd() {
System.out.println("测试用例testMyAdd():");
Assert.assertEquals(3,calculator.myAdd(1,2),"加法函数运行失败!");
}
@Test
public void testMySub() {
System.out.println("测试用例testMySub():");
Assert.assertEquals(3,calculator.mySub(5,2),"减法函数运行失败!");
}
@AfterClass
public void afterClass() {
System.out.println(" @AfterClass在测试类运行之后运行:");
}
}
在测试代码界面,右击鼠标,点击Run 'CalculatorTest'
:
结果:
[TestNG] Running:
C:\Users\yuyang2\AppData\Local\Google\AndroidStudio4.1\temp-testng-customsuite.xml
@BeforeClass在测试类运行之前运行:
执行每个用例前执行setUp():
测试用例testMyAdd():
执行每个用例后执行tearDown():
执行每个用例前执行setUp():
测试用例testMySub():
执行每个用例后执行tearDown():
@AfterClass在测试类运行之后运行:
===============================================
Default Suite
Total tests run: 2, Failures: 0, Skips: 0
===============================================
Process finished with exit code 0
结论:
- 在每个测试用例执行前后都会先执行被 @BeforeMethod 和 @AfterMethod注解的方法;
- 在测试类执行前后会先后执行一次被@BeforeClass和@AfterClass注解的方法。
2.2 套件测试
- TestNG的套件管理有点“特别”,它是以一个xml文件作为统一配置文件的,一般会命名为
testNG.xml
,实际上文件的命名随意,you happy just ok! - 执行时通过运行xml文件
- 最基本的套件管理规则时:
suite->test->classes->class
- 同一个test下的测试类看做是一个整体,其中的注解对整个test整体都是生效的
下面看实操演示,本次用到3个测试类SuiteTest1、SuiteTest2和SuiteTestConfig
在assets文件夹下创建套件配置文件testNG.xml
2.2.1 @BeforeSuite、@AfterSuite、@BeforeTest和@AfterTest注解方法
- 测试脚本:
SuiteTest1.java
package yuyang.example.hellowng;
import org.testng.annotations.Test;
public class SuiteTest1 {
@Test
public void suiteTest1() {
System.out.println("测试用例 suiteTest1():");
}
}
- 测试脚本:
SuiteTest2.java
package yuyang.example.hellowng;
import org.testng.annotations.Test;
public class SuiteTest2 {
@Test
public void suiteTest2() {
System.out.println("测试用例 suiteTest2():");
}
}
- 测试脚本:
SuiteTestConfig.java
package yuyang.example.hellowng;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class SuiteTestConfig {
@BeforeSuite
public void beforeSuiteTest(){
System.out.println("beforeSuiteTest() 在测试套件运行前执行:");
}
@AfterSuite
public void afterSuiteTest(){
System.out.println(" afterSuiteTest() 在测试套件运后执行:");
}
@BeforeTest
public void beforeTest(){
System.out.println("beforeTest() 在每个测试脚本Test前执行:");
}
@AfterTest
public void afterTest(){
System.out.println(" afterTest() 在每个测试脚本Test后执行:");
}
@Test
public void SuiteTestConfigCase() {
System.out.println(" 我是SuiteTestConfig测试Case:");
}
}
-xml配置文件: testNG.xml
:
在配置文件testNG.xml
中配置套件执行顺序
将SuiteTest1、SuiteTestConfig
"包"成一个test整体,将SuiteTest2、SuiteTestConfig
"包"成一个test整体;然后依顺序执行。
注:suite和test Tag需要给一个name,否则会报错
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="testNG Suite">
<test name="CalculatorTest">
<classes>
<class name="yuyang.example.hellowng.SuiteTest1"></class>
<class name="yuyang.example.hellowng.SuiteTestConfig"></class>
</classes>
</test>
<test name="FunctionTest">
<classes>
<class name="yuyang.example.hellowng.SuiteTest2"></class>
<class name="yuyang.example.hellowng.SuiteTestConfig"></class>
</classes>
</test>
</suite>
在testNG.xml
界面右击,Run
结果:
[TestNG] Running:
D:\AndroidStudioProjects\hellowNG\app\src\androidTest\assets\testNG.xml
beforeSuiteTest() 在测试套件运行前执行:
beforeTest() 在每个测试脚本Test前执行:
测试用例 suiteTest1():
我是SuiteTestConfig测试Case:
afterTest() 在每个测试脚本Test后执行:
beforeTest() 在每个测试脚本Test前执行:
测试用例 suiteTest2():
我是SuiteTestConfig测试Case:
afterTest() 在每个测试脚本Test后执行:
afterSuiteTest() 在测试套件运后执行:
===============================================
testNG Suite
Total tests run: 4, Failures: 0, Skips: 0
===============================================
Process finished with exit code 0
结论:
- @BeforeSuite和@AfterSuite注解的方法,仅仅在suite执行前后分别执行一次;
- 每个test执行前后都会先后执行一次由@BeforeTest、@AfterTest注解的方法;
2.2.2 忽略测试@Test(enable=false)
@Test(enabled = false)
public void testMySub() {
System.out.println("测试用例testMySub():");
Assert.assertEquals(3,calculator.mySub(5,2),"减法函数运行失败!");
}
结果:不执行该Test用例
[TestNG] Running:
C:\Users\yuyang2\AppData\Local\Google\AndroidStudio4.1\temp-testng-customsuite.xml
@BeforeClass在测试类运行之前运行:
执行每个用例前执行setUp():
测试用例testMyAdd():
执行每个用例后执行tearDown():
@AfterClass在测试类运行之后运行:
===============================================
2.3 分组测试
2.3.1 方法分组 @Test(groups=“xxx”)、@BeforeGroups、@AfterGroups
- 分别将方法test1和test2分为“测试1组”和“测试2组”
- 再在测试1组执行前执行@BeforeGroups注解方法,在测试2组执行后执行@AfterGroups注解方法
package yuyang.example.hellowng;
import org.testng.Assert;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterClass;
public class CalculatorTest {
Calculator calculator = new Calculator();
@BeforeClass
public void beforeClass(){
System.out.println("@BeforeClass在测试类运行之前运行:");
}
@BeforeMethod
public void setUp() {
System.out.println("执行每个用例前执行setUp():");
}
@AfterMethod
public void tearDown() {
System.out.println("执行每个用例后执行tearDown():");
}
@Test(groups = "测试1组")
public void testMyAdd() {
System.out.println("测试用例testMyAdd():");
Assert.assertEquals(3,calculator.myAdd(1,2),"加法函数运行失败!");
}
@Test(groups = "测试2组")
public void testMySub() {
System.out.println("测试用例testMySub():");
Assert.assertEquals(3,calculator.mySub(5,2),"减法函数运行失败!");
}
@AfterClass
public void afterClass() {
System.out.println(" @AfterClass在测试类运行之后运行:");
}
@BeforeGroups("测试1组")
public void beforeGroups(){
System.out.println(" @BeforeGroups在 测试1组方法testMyAdd()之前运行:");
}
@AfterGroups("测试2组")
public void afterGroups(){
System.out.println(" @AfterGroups在 测试2组方法testMySub()之后运行:");
}
}
结果:
[TestNG] Running:
C:\Users\yuyang2\AppData\Local\Google\AndroidStudio4.1\temp-testng-customsuite.xml
@BeforeClass在测试类运行之前运行:
@BeforeGroups在 测试1组方法testMyAdd()之前运行:
执行每个用例前执行setUp():
测试用例testMyAdd():
执行每个用例后执行tearDown():
执行每个用例前执行setUp():
测试用例testMySub():
执行每个用例后执行tearDown():
@AfterGroups在 测试2组方法testMySub()之后运行:
@AfterClass在测试类运行之后运行:
===============================================
Default Suite
Total tests run: 2, Failures: 0, Skips: 0
===============================================
2.3.2 测试类分组 @Test(groups=“xxx”)
- 当前有3个测试类
ClassGroups1Test、ClassGroups2Test、ClassGroups3Test
- 1)分别将这3个测试类进行分组
Group1、Group2、Group3
- 2)将这3个测试类以3、2、1的执行顺序引入xml套件配置文件
<classes>
<class name="yuyang.example.hellowng.groups.ClassGroups3Test"></class>
<class name="yuyang.example.hellowng.groups.ClassGroups2Test"></class>
<class name="yuyang.example.hellowng.groups.ClassGroups1Test"></class>
</classes>
3)设置场景,利用配置<groups>-><run>-><include>/<exclude>
让Group1
和Group3
执行,Group2
不执行(实际上如果<groups>
中直接不写Group2
,它也不会执行)
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="testNG Suite">
<test name="testGroup">
<groups>
<run>
<include name="Group1"/>
<exclude name="Group2"/>
<include name="Group3"/>
</run>
</groups>
<classes>
<class name="yuyang.example.hellowng.groups.ClassGroups3Test"></class>
<class name="yuyang.example.hellowng.groups.ClassGroups2Test"></class>
<class name="yuyang.example.hellowng.groups.ClassGroups1Test"></class>
</classes>
</test>
</suite>
结果:
结论·:
- 测试类分组在Group3和Group1的方法依次执行了
- Group2分组中的测试类未被执行
测试用例的逻辑顺序设计合理,使用分组的频率不高
2.4 异常测试@Test(exceptedExceptions = XXXException.class)
测试时,我们可能期望的结果就是抛出某种异常,比如单元测试时输入非法入参,程序期望抛出异常,而这是期望的正确结果,我们希望用例是测试通过的,这时就需要用到异常测试注解:@Test(exceptedExceptions = XXXException.class)
package yuyang.example.hellowng.groups;
import org.testng.annotations.Test;
public class SuiteTest {
@Test(expectedExceptions = RuntimeException.class)
public void RuntimeExceptionFail() {
System.out.println("Group1中 groups1Test1执行");
}
@Test(expectedExceptions = RuntimeException.class)
public void RuntimeExceptionSuccess() {
System.out.println("Group1中 groups1Test1执行");
throw new RuntimeException();
}
}
结果:
注: 单元测试平常更多的可能由研发人员自己完成,一般功能和接口测试,测试工程师期待的都是后端对异常处理后返回的一个状态码code和message信息
2.5 依赖测试@Test(dependsOnMethods = {“funtion name”})
有时候一个用例的执行要依赖其他用例的执行结果,例如购买商品前需要依赖用户登录成功才可以,这个时候就需要使用@Test(dependsOnMethods = {"funtion name"})
对另一个用例进行依赖
- 依赖测试成功案例
package yuyang.example.hellowng.groups;
import org.testng.annotations.Test;
public class ClassGroups2Test {
@Test
public void login(){
System.out.println("登录成功!");
}
@Test(dependsOnMethods = "login")
public void pay(){
System.out.println("购买商品成功!");
}
}
结果:
[TestNG] Running:
C:\Users\yuyang2\AppData\Local\Google\AndroidStudio4.1\temp-testng-customsuite.xml
登录成功!
购买商品成功!
===============================================
Default Suite
Total tests run: 2, Failures: 0, Skips: 0
===============================================
Process finished with exit code 0
结论:执行了pay方法,但是由于pay方法是依赖于login方法的,所以会先执行login方法
- 依赖测试失败案例
package yuyang.example.hellowng.groups;
import org.testng.annotations.Test;
public class ClassGroups2Test {
@Test
public void login(){
System.out.println("登录成功!");
}
@Test(dependsOnMethods = "login")
public void pay(){
System.out.println("购买商品成功!");
}
}
结果:
结论:
- 被依赖的用例执行失败,后面的用例会直接跳过忽略
- 测试结果显示为忽略而不是失败,这样当有成百上千条用例因为被依赖的用例失败而执行不通过时,可以只排查被依赖用例失败原因即可;否则如Junit4全部标记为失败的话会造成排查问题和回归测试效率的极大浪费
2.6 参数化测试
- 有的方法需要传参,好比登录成功时我们需要用户的姓名和ID号
- 参数的传递直接写在代码中不利于维护更改,也不方便不懂代码的测试人员进行参数修改,这个时候就需要参数化测试
2.6.1 参数化测试1:@Parameters,在xml中传递参数
需要在方法上加上注解@Parameters
,并在xml配置文件中利用<parameter name="xx" value="xxx"/>
的方式传参。
注:也可用<methods><methods/>tag
对指定的方法进行参数传递
- 测试脚本
SuiteTest1.java
package yuyang.example.hellowng;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
public class SuiteTest1 {
@Test
@Parameters({"name","id"})
public void login(String name, String id){
System.out.println(name+"登录成功!" + "编号:"+id);
}
@Test(dependsOnMethods = "login")
public void pay(){
System.out.println("购买商品成功!");
}
}
- xml里完成传参:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="testNG Suite">
<test name="ParametersTest">
<classes>
<parameter name="name" value="yuyang"></parameter>
<parameter name="id" value="22"></parameter>
<class name="yuyang.example.hellowng.SuiteTest1"></class>
</classes>
</test>
</suite>
2.6.2 参数化测试2:@Test(dataProvider = “name”)+@DataProvider
1)利用@Test(dataProvider = "name")+@DataProvider(name="name")
将多组数据传递到一个方法中依次执行
package yuyang.example.hellowng;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class SuiteTest2 {
@Test(dataProvider = "dataInfo")
public void userInfo(String name, String id){
System.out.println(name+"登录成功!" + "编号:"+id);
}
@DataProvider(name = "dataInfo")
public Object[][] dataProvider(){
Object[][] objects = new Object[][]{
{"Allen", "001"},
{"Mary", "002"},
{"Bob", "003"}
};
return objects;
}
}
结果:
2)利用·@Test(dataProvider = "name")+@DataProvider(name="name")
指定测试方法,传递指定入参进行测试
package yuyang.example.hellowng;
import java.lang.reflect.Method;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class SuiteTest1 {
@Test(dataProvider = "dataInfoMethod")
public void userInfo1(String name, String id){
System.out.println(name+"登录成功!" + "编号:"+id);
}
@Test(dataProvider = "dataInfoMethod")
public void userInfo2(String name, String id){
System.out.println(name+"登录成功!" + "编号:"+id);
}
@DataProvider(name = "dataInfoMethod")
public Object[][] dataProviderMethod(Method method){
if (method.getName().equals("userInfo1")){
return new Object[][]{{"Allen", "001"}};
}
else if (method.getName().equals("userInfo2")){
return new Object[][]{{"Mary", "002"}};
}
return null;
}
}
分别单独运行方法userInfo1
和userInfo2
得到测试结果:
userInfo1:
userInfo2:
2.7 多线程测试
2.7.1 多线程测试注解实现 @Test(invocationCount=10,threadPoolSize=4)
参数说明: 官方给出的解释是如下
简单来说就是:
-
invocationCount
表示方法要运行几次 -
threadPoolSize
表示线程池大小,且要配合invocationCount才起作用
现在将userInfo1
方法用多线程执行10次,线程池大小设为4,打印当前线程id以观察验证
测试代码:
@Test(dataProvider = "dataInfoMethod", invocationCount = 10,threadPoolSize = 4)
public void userInfo1(String name, String id){
System.out.println(name+"登录成功!" + "编号:"+id +" Current Thread id: "+Thread.currentThread().getId());
}
结果:
从测试结果中可以看到4个不同的线程一共将方法userInfo1
执行了10次。
2.7.2 多线程测试xml实现-parallel(methods|tests|classes)+thread-count
参数解释: 官方文档的解释如下:
- parallel(methods|tests|classes):设置使用多线程,且有methods|tests|classes三种不同级别选择
- methods: 所有用例都可以在不同的线程下执行,包括依赖的用例
- tests: 同一个
<test>
tag中的用例运行在同一个线程下,不同<test>
tag中的用例可以运行在不同线程下 - classes: 同一个
<class>
tag中的用例运行在同一个线程下,不同<class>
tag中的用例可以运行在不同线程下
1)创建3个方法,打印线程IDSuiteTest3.java:
package yuyang.example.hellowng;
import org.testng.annotations.Test;
public class SuiteTest3 {
@Test
public void threadTest1(){
System.out.println("Test3 Thread is: "+ Thread.currentThread().getId());
}
@Test
public void threadTest2(){
System.out.println("Test3 Thread is: "+ Thread.currentThread().getId());
}
@Test
public void threadTest3(){
System.out.println("Test3 Thread is: "+ Thread.currentThread().getId());
}
}
2)
- methods-所有用例都可以在不同的线程下执行
设置parallel为methods级别,thread-count为3,进行测试testNG.xml:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="testNG Suite" parallel="methods" thread-count="3">
<test name="ThreadTest">
<classes>
<class name="yuyang.example.hellowng.SuiteTest3"></class>
</classes>
</test>
</suite>
结果:
-
tests-
同一个<test>
中的用例运行在同一个线程下,不同<test>
中的用例可以运行在不同线程下 再创建测试类SuiteTest4
,添加三个方法并打印thread ID
SuiteTest4.java:
package yuyang.example.hellowng;
import org.testng.annotations.Test;
public class SuiteTest4 {
@Test
public void threadTest1(){
System.out.println("Test4 Thread is: "+ Thread.currentThread().getId());
}
@Test
public void threadTest2(){
System.out.println("Test4 Thread is: "+ Thread.currentThread().getId());
}
@Test
public void threadTest3(){
System.out.println("Test4 Thread is: "+ Thread.currentThread().getId());
}
}
结果:
Test4 Thread is: 13
Test3 Thread is: 12
Test4 Thread is: 13
Test3 Thread is: 12
Test3 Thread is: 12
Test4 Thread is: 13
===============================================
testNG Suite
Total tests run: 6, Failures: 0, Skips: 0
===============================================
Process finished with exit code 0
-
classes-
同一个<class>
中的用例运行在同一个线程下,不同中的用例可以运行在不同线程下
设置paralle
l为classes
级别,thread-count
为2,进行测试
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="testNG Suite" parallel="classes" thread-count="2">
<test name="ThreadTest3">
<classes name="3">
<class name="yuyang.example.hellowng.SuiteTest3"></class>
</classes>
</test>
<test name="ThreadTest4">
<classes name="4">
<class name="yuyang.example.hellowng.SuiteTest4"></class>
</classes>
</test>
</suite>
结果:
Test3 Thread is: 12
Test3 Thread is: 12
Test3 Thread is: 12
Test4 Thread is: 13
Test4 Thread is: 13
Test4 Thread is: 13
===============================================
testNG Suite
Total tests run: 6, Failures: 0, Skips: 0
===============================================
Process finished with exit code 0
注:
虽然框架本身说明了多线程是安全的,但是由于我们自身编码可能不能保证严格规范,容易造成多线程不安全,所以建议不要适用多线程测试,而是适用多进程测试。