白盒测试testng

2019/10/05 TestNG

白盒测试TestNG

TestNG是一套开源测试框架,是从Junit继承而来,testng意为test next generation TestNG是一个设计用来简化广泛的测试需求的测试框架,从单元测试(隔离测试一个类)到集成测试(测试由有多个类多个包甚至多个外部框架组成的整个系统,例如运用服务器)。

概念

TestNG是一个测试框架,其灵感来自JUnit和NUnit,但引入了一些新的功能,使其功能更强大,使用更方便。 TestNG是一个开源自动化测试框架;TestNG表示下一代(Next Generation的首字母)。 TestNG类似于JUnit(特别是JUnit 4),但它不是JUnit框架的扩展。它的灵感来源于JUnit。它的目的是优于JUnit,尤其是在用于测试集成多类时。 TestNG的创始人是Cedric Beust(塞德里克·博伊斯特)。 TestNG消除了大部分的旧框架的限制,使开发人员能够编写更加灵活和强大的测试。 因为它在很大程度上借鉴了Java注解(JDK5.0引入的)来定义测试,它也可以显示如何使用这个新功能在真实的Java语言生产环境中。

特点

  • 注解
  • TestNG使用Java和面向对象的功能
  • 支持综合类测试(例如,默认情况下,不用创建一个新的测试每个测试方法的类的实例)
  • 独立的编译时测试代码和运行时配置/数据信息
  • 灵活的运行时配置
  • 主要介绍“测试组”。当编译测试,只要要求TestNG运行所有的“前端”的测试,或“快”,“慢”,“数据库”等
  • 支持依赖测试方法,并行测试,负载测试,局部故障
  • 灵活的插件API
  • 支持多线程测试

【官网】

https://testng.org/doc/index.html

【简介】

TestNG 是 Java中一个很流行实用的单元测试框架。

它的灵感来源于 Junit ( java 的单元测试框架) 和 Nunit ( .net 的单元测试框架)。

但是它又在此基础上引入了新的东西,使得它更加强大。

关于注解特性方面,可以参考下表:

特性 JUnit 4 TestNG 测试注解 @Test @Test 测试套件在执行之前需要执行的 – @BeforeSuite 测试套件在执行之后需要执行的 – @AfterSuite 在测试之前需要执行的 – @BeforeTest 在测试之后需要执行的 – @AfterTest 在一个测试方法所属于的任意一个组的第一个方法被调用之前执行 – @BeforeGroups 在一个测试方法所属于的任意一个组的最后一个方法被调用之后执行 – @AfterGroups 在当前类的第一个测试方法调用之前执行 @BeforeClass @BeforeClass 在当前类的最后一个测试方法调用之后执行 @AfterClass @AfterClass 每个测试方法之前需要执行 @Before @BeforeMethod 每个测试方法之后需要执行 @After @AfterMethod 忽略 @ignore @Test(enbale=false) 预期异常 @Test(expected = ArithmeticException.class) @Test(expectedExceptions = ArithmeticException.class) 超时 @Test(timeout = 1000) @Test(timeout = 1000) TestNG 和 JUnit 还有两个比较明显的区别:

在Junit 4 中,如果我们需要在方法前面使用 @BeforeClass 和 @AfterClass ,那么该测试方法则必须是静态方法。TestNG 在方法定义部分则更加的灵活,它不需要类似的约束。 TestNG 中子类不会运行父类中的 @BeforeClass 和 @AfterClass, 而在Junit中会先运行父类的@BeforeClass,再运行自己的 @BeforeClass;而 @AfterClass 是先运行自己的,再运行父类的。 经过一番对比之后,TestNG 在参数化测试、依赖测试以及套件测试(组)方面功能比 Junit 更加强大,并且包含了几乎 Junit 的所有功能,所以建议优先选择 TestNG。

【使用场景】

常用于单元测试(白盒测试) 自动化测试的用例管理和用例执行框架(API自动化、UI自动化)。

创建maven项目,添加依赖

<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>6.14.3</version>
    <scope>test</scope>
</dependency>

常用注解

@BeforeSuite / @AfterSuite @BeforeTest / @AfterTest @BeforeClass / @AfterClass,在类运行之前/后运行 @BeforeMethod / @AfterMethod,在测试方法之前/后运行

import org.testng.annotations.*;
import org.testng.annotations.Test;
public class TestAnnotation {

    @Test
    public  void test(){
        System.out.println("TestAnnotation.test");
        System.out.println("线程ID:" + Thread.currentThread().getId());
    }

    @Test
    public  void test2(){
        System.out.println("TestAnnotation.test2");
    }

    @BeforeMethod
    public void beforeMethodTest(){
        System.out.println("TestAnnotation.beforeMethodTest");
    }

    @AfterMethod
    public void afterMethodTest(){
        System.out.println("TestAnnotation.afterMethodTest");
    }

    @BeforeClass
    public void beforeClassTest(){
        System.out.println("TestAnnotation.beforeClassTest");
    }

    @AfterClass
    public void afterClassTest(){
        System.out.println("TestAnnotation.afterClassTest");
    }

    @BeforeSuite
    public void beforeSuiteTest(){
        System.out.println("TestAnnotation.beforeSuiteTest");
    }

    @AfterSuite
    public void afterSuiteTest(){
        System.out.println("TestAnnotation.afterSuiteTest");
    }
}

打印日志

TestAnnotation.beforeSuiteTest
TestAnnotation.beforeClassTest
TestAnnotation.beforeMethodTest
TestAnnotation.test
线程ID:1
TestAnnotation.afterMethodTest
TestAnnotation.beforeMethodTest
TestAnnotation.test2
TestAnnotation.afterMethodTest
TestAnnotation.afterClassTest
TestAnnotation.afterSuiteTest

Test注解

@Test就这么简单?No,它的精华在于它的以下几个参数。

  • groups:组测试,一个test属于哪一个组,可以跑一个特定组的所有test
  • dependsOnMethods、dependsOnGroups:依赖测试,一个test依赖于另一个test的执行结果
  • expectedExceptions:异常测试
  • dataProvider:参数化测试,将参数传入该test
  • enabled:忽略测试,不执行该test
  • timeOut、threadPoolSize、invocationCount、successPercentage:并发测试,设置并发测试的各种参数
  • alwaysRun:如果为true的话,不管怎样都会运行

@BeforeSuite: 被注释的方法将在所有测试运行前运行 @AfterSuite: 被注释的方法将在所有测试运行后运行 @BeforeTest: 被注释的方法将在测试运行前运行 @AfterTest: 被注释的方法将在测试运行后运行 @BeforeGroups: 被配置的方法将在列表中的gourp前运行。这个方法保证在第一个属于这些组的测试方法调用前立即执行。 @AfterGroups: 被配置的方法将在列表中的gourp后运行。这个方法保证在最后一个属于这些组的测试方法调用后立即执行。 @BeforeClass: 被注释的方法将在当前类的第一个测试方法调用前运行。 @AfterClass: 被注释的方法将在当前类的所有测试方法调用后运行。 @BeforeMethod: 被注释的方法将在每一个测试方法调用前运行。 @AfterMethod: 被注释的方法将在每一个测试方法调用后运行。 属性: alwaysRun 对于每个bufore方法(beforeSuite, beforeTest, beforeTestClass 和 beforeTestMethod, 但是不包括 beforeGroups): 如果设置为true,被配置的方法将总是运行而不管它属于哪个组。 对于after方法(afterSuite, afterClass, …): 如果设置为true,被配置的方法甚至在一个或多个先调用的方法失败或被忽略时也将运行。 dependsOnGroups 这个方法依赖的组列表 dependsOnMethods 这个方法依赖的方法列表 enabled 这个类的方法是否激活 groups 这个类或方法所属的分组列表 inheritGroups 如果设置为true,这个方法被属于在类级别被@Test annotation指定的组

@DataProvider 标记一个方法用于为测试方法提供数据。 被注释的方法必须返回Object[][], 其中每个Object[]可以指派为这个测试方法的参数列表。 从这个DataProvider接收数据@Test方法需要使用一个和当前注释相同名称的dataProvider名称 name 这个DataProvider的名称

@Factory 标记方法作为一个返回对象的工厂,这些对象将被TestNG用于作为测试类。这个方法必须返回Object[]

@Parameters 描述如何传递参数给@Test方法 value 用于填充这个方法的参数的变量列表

@Test 标记一个类或方法作为测试的一部分 alwaysRun 如果设置为true,这个测试方法将总是运行,甚至当它依赖的方法失败时。 dataProvider 这个测试方法的data provider的名称 dataProviderClass 用于查找data provider的类。 如果不指定,将在当前测试方法所在的类或者它的基类上查找data provider。 如果这个属性被指定, 则data provider方法需要是指定类的static方法。 dependsOnGroups 当前方法依赖的组列表 dependsOnMethods 当前方法依赖的方法列表 description 当前方法的描述 enabled 当前类的方法/方法是否被激活 expectedExceptions 测试方法期望抛出的异常列表。如果没有异常或者抛出的不是列表中的任何一个,当前方法都将标记为失败. groups 当前类/方法所属的组列表 invocationCount 当前方法被调用的次数 successPercentage 当前方法期望的成功率 sequential 如果设置为true,当前测试类上的所有方法保证按照顺序运行。甚至测试们在parallel=”true”的情况下. 这个属性只能用于类级别,如果用于方法级别将被忽略。 timeOut 当前方法容许花费的最大时间,单位毫秒。 threadPoolSize 当前方法的线程池大小。方法将被多线程调用,次数由invocationCount参数指定 注意:如果invocationCount没有指定则这个属性将被忽略

注: 上面是TestNG中用到的annotation列表,从中我们可以看到TestNG提供的一些特性

  1. before方法和after方法 带来了足够丰富的测试生命周期控制
  2. dependsOnGroups/dependsOnMethods 提供了依赖检查机制,并可以严格控制执行顺序
  3. DataProvider 使得对同一个方法的测试覆盖变的非常轻松,非常适合进行边界测试,只要给出多种测试数据就可以针对一个测试方法进行覆盖
  4. expectedExceptions 使得异常测试变的非常轻松
  5. invocationCount/threadPoolSize 终于可以简单的直接进行多线程测试了,这个绝对是junit的超级弱项,回想junit中那个万恶的System.exist(0)…
  6. timeOut 终于不用死等然后手工强行关闭测试,TestNG想的太周到了

TestNG XML

创建xml文件 在resources下创建suite.xml,文件名随意,只要内容符合要求就可以了 suite:套件,包含一个或多个test   test:测试集,包含一个或多个classes     classes:测试类集合,包含一个或多个class       class:测试类,包含一个或多个方法

<?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="test">-->
    <test name="test">
        <classes>
            <class name="com.testng.TestAnnotation"/>
        </classes>
    </test>
</suite>

忽略测试

测试过程中,问题还没解决,可以先忽略,也就是不执行此方法

import org.testng.annotations.Test;

public class TestIgnore {
    @Test
    public void testa(){
        System.out.println("TestIgnore.testa");
    }

    @Test(enabled = true)
    public void testb(){
        System.out.println("TestIgnore.testb");
    }

    @Test(enabled = false)
    public void testc(){
        System.out.println("TestIgnore.testc");
    }
}

运行结果:

TestIgnore.testa
TestIgnore.testb

分组测试

场景︰只想执行个别或者某一部分的测试用例

import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
public class TestGroups {

    @Test(groups = "login")
    public void testa(){
        System.out.println("TestIgnore.testa");
    }

    @Test(groups = "submitOrder")
    public void testb(){
        System.out.println("TestIgnore.testb");
    }

    @Test(groups = "submitOrder")
    public void testc(){
        System.out.println("TestIgnore.testc");
    }

    @BeforeGroups("submitOrder")
    public void testBeforeGroups(){
        System.out.println("TestGroups.testBeforeGroups");
    }

    @AfterGroups("submitOrder")
    public void testAfterGroup(){
        System.out.println("TestGroups.testAfterGroup");
    }
}

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="test">-->
    <test name="test">  <!--test必须有name属性-->
        <groups>
            <run>
                <include name="submitOrder"/>
            </run>
        </groups>
        <classes>
            <class name="com.testng.TestGroups"/>
        </classes>
    </test>
</suite>

输出结果:

TestGroups.testBeforeGroups
TestIgnore.testb
TestIgnore.testc
TestGroups.testAfterGroup

依赖测试

dependsOnMethods和BeforeMethod的区别是: BeforeMethod是每个方法前都要执行,而dependsOnMethods只是依赖的方法前执行

import org.testng.annotations.Test;
public class TestDepend {
    @Test(dependsOnMethods = {"test2"})
    public  void test(){
        System.out.println("TestAnnotation.test");
    }

    @Test
    public  void test2(){
        System.out.println("TestAnnotation.test2");
    }
}

输出日志

TestAnnotation.test2
TestAnnotation.test

如果被依赖方法执行失败,有依赖关系的方法不会被执行;

应用场景,登录失败,就不能进行下单等操作

import org.testng.annotations.Test;
 

public class TestDepend {
    @Test(dependsOnMethods = {"test2"})
    public  void test(){
        System.out.println("TestAnnotation.test");
    }
 
    @Test
    public  void test2(){
        System.out.println("TestAnnotation.test2");
        throw new RuntimeException();  // 抛出一个异常
    }
}

超时

timeout属性的单位为毫秒。

import org.testng.annotations.Test;

public class TestTimeOut {


    @Test(timeOut = 1000)  // 单位为毫秒值,期望在1秒内得到结果
    public void test() throws InterruptedException {
        System.out.println("TestTimeOut.test");
        Thread.sleep(500);
    }

    @Test(timeOut = 1000)
    public void test2() throws InterruptedException {
        System.out.println("TestTimeOut.test2");
        for (int i = 10; i > 0; i--) {
            Thread.sleep(101);
            System.out.println(i);
        }
        System.out.println("执行结束。");
    }
}

参数化(数据驱动测试)

两种方式向测试方法传递参数:

利用testng.xml定义parameter

利用DataProviders

xml文件参数化

import org.testng.annotations.Parameters;
import org.testng.annotations.Test;

public class TestParameter {

    @Test
    @Parameters({"name","id"})
    public void test(String name, int id){
        System.out.println("name=" + name + ", id=" + id);
    }
}
<?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="test">-->
    <test name="test">  <!--test必须有name属性-->
        <classes>
            <class name="com.testng.TestParameter">
                <parameter name="name" value="admin"/>
                <parameter name="id" value="1"/>
            </class>
        </classes>
    </test>
</suite>

DataProvider参数化


import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class TestDataProvider {

    @Test(dataProvider="data")  // 和下面的name对应起来
    public void testDataProvider(String name, int id){
        System.out.println("name=" + name + ", id=" + id);
    }
    @DataProvider(name = "data")  // 如果没有指定name,上面就写下面的方法名:providerData
    public Object[][] providerData(){
        Object[][] datas = new Object[][]{
                {"zhangsan",1001},
                {"lisi",1002},
                {"wangwu",1003}
        };
        return datas;
    }
}

TestNG监听器

ITestListener监听器主要针对测试方法和xml文件中的test标签来进行相应监控。

ITestListener监听器

onTestStart():测试方法开始时才执行此方法。这里测试方法是指@Test注解的测试方法; onTestSuccess():如果测试方法执行成功才执行此方法; onTestFailure():如果测试方法执行失败才执行此方法; onTestSkipped():如果测试方法执行跳过才执行此方法; onTestFailedButWithinSuccessPercentage():自定义测试方法的执行结果。比如测试方法中有多个断言,其中一个断言没有通过,其他的断言都通过了,你可以根据具体情况,判断此测试方法执行通过; onStart():在xml文件中的每一个标签跑之前运行; onFinish():在xml文件中的每一个标签跑之后运行。

@Listeners(CustomListener2.class)
public class TestNGTestListener2 {
    @BeforeClass
    public void setUp(){
        System.out.println("TestNGTestListener2类中的setUp方法");
    }
    @AfterClass
    public void tearDown(){
        System.out.println("TestNGTestListener2类中的tearDown方法");
    }
    @Test
    public void testMethod1(){
        System.out.println("TestNGTestListener2类中的testMethod1方法");
        Assert.assertTrue(true);
    }
    @Test
    public void testMethod2(){
        System.out.println("TestNGTestListener2类中的testMethod2方法");
        Assert.assertFalse(true);
    }
}

如何生成测试报告

reportng是替代testng报告的一个报告插件,优势在于美观。不过1.1.4版本是reportng的最后一个版本,已经不再更新了。


<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>3.0</version>
</dependency>

<dependency>
    <groupId>org.uncommons</groupId>
    <artifactId>reportng</artifactId>
    <version>1.1.4</version>
    <exclusions>
        <exclusion>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
        </exclusion>
    </exclusions>
</dependency>

首先,我们要先准备好使用ReportNG所以依赖的包: ReportNG1.4.1.jar:我们所使用的报告输出的html模板就在这个包里,包括css样式和JS文件,我们都可以进行二次开发重现编译来输出我们想要看到的报告。 velocity-dep-1.4.jar:ReportNG所依赖的包。 guice-3.0.jar:网上资料基本上都遗漏这个包,但是缺少这个包,会获取不到数据,具体作用自行研究 在导入这个包后,我们需要在testng.xml里加上监听器,依次分别是HTML形式的报告监听器、xml形式的报告监听器、截图的监听器(两种方式选一种即可)。

testng.xml中配置listener:

<listeners>
    <listener class-name="org.uncommons.reportng.HTMLReporter" />
    <listener class-name="org.uncommons.reportng.JUnitXMLReporter" />
    <listener class-name="com.edusoho.util.ScreenFailtureListener"/>
</listeners>

还有一种值得注意的,我们最好关闭testng自带的输出报告的监听器,不然会生成大量的文件,但是不影响ReportNG输出报告,ReportNG输出的测试报告一般的test-out/html文件下。本地一般是默认不打开的监听器,不需要进行额外的操作,如果有可以通过以下配置来关闭。

<property>
  <name>userdefaultlisteners</name>
  <value>false</value>
</property>

这个里输出报告,需要在相应的你想要输出日志的地方调用Reportng.log()方法来输出日志。

集成maven

实现方式:集成maven的surefire插件。surefire插件用于maven的test阶段,以执行单元测试,集成后我们就可以通过maven命令–maven test来调用脚本执行了。

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.17</version>
            <configuration>
                <suiteXmlFiles>
                    <suiteXmlFile>xmlfile/testng.xml</suiteXmlFile>
                </suiteXmlFiles>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19.1</version>
            <configuration>
                <properties>
                    <property>
                        <name>usedefaultlisteners</name>
                        <value>false</value>
                    </property>
                    <property>
                        <name>listener</name>
                        <value>org.uncommons.reportng.HTMLReporter, org.uncommons.reportng.JUnitXMLReporter</value>
                    </property>
                </properties>
                <workingDirectory>target/</workingDirectory>
            </configuration>
        </plugin>
    </plugins>
</build>

右键run maven-test 可以在本地的文件目录下查看测试报告

参考资料

TestNG https://testng.org/doc/index.html

Search

    微信好友

    博士的沙漏

    Table of Contents