TestNG框架 在AndroidStudio上部署单元测试

1.什么是TestNG

TestNG是JAVA的一个单元测试框架,类似于JUnit,是一种注解式的编程方式。和Junit主要的区别和适合测试工程师使用的原因:

  • TestNG是比Junit涵盖功能更全面的测试框架,具有参数化和分组的特性,可以做数据驱动
  • Junit更适合隔离性比较强的单元测试
  • TestNG被设计应与覆盖所有的测试,单元、功能、端到端、集成测试等
  • TestNG依赖测试时对于依赖方法失败后的用例标记为跳过而不是像Junit标记为失败,减少失败原因排查成本
  • TestNG可以针对失败用例回归测试,增加测试针对性和效率,而Junit需要将所有测试用例重新执行
  • TestNG更适合测试工程师需要的大范围的复杂的继集成测试

TestNG官方说明:https://testng.org/doc/documentation-main.html
IBM 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>Group1Group3执行,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;
    }
}

分别单独运行方法userInfo1userInfo2得到测试结果:
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个方法,打印线程ID
SuiteTest3.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>中的用例运行在同一个线程下,不同中的用例可以运行在不同线程下

设置parallel为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

注:
虽然框架本身说明了多线程是安全的,但是由于我们自身编码可能不能保证严格规范,容易造成多线程不安全,所以建议不要适用多线程测试,而是适用多进程测试。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值