美文网首页
TestNG - 单元测试框架 quickStart

TestNG - 单元测试框架 quickStart

作者: 虐心笔记 | 来源:发表于2020-08-15 12:59 被阅读0次

    一、概述

    JUnit 框架是 Java 语言单元测试当前的一站式解决方案。这个框架值得称赞,因为它把测试驱动的开发思想介绍给 Java 开发人员并教给他们如何有效地编写单元测试。但是,在过去的几年中,JUnit 的改进不大;所以,为当今复杂的环境编写测试已经变成一个越来越困难的任务,即 JUnit 必须与其他一些补充性测试框架集成起来。而 TestNG 是一个测试 Java 应用程序的新框架。TestNG 不仅确实强大、创新、可扩展、灵活,它还展示了 Java Annotations(JDK 5.0 中的重大新特性)的有趣应用。TestNG 的创造者是 Cedric Beust,他在 Java 编程领域非常出名,是 EJB 3 专家组的成员,也是其他一些流行的开源项目(例如 EJBGen 和 Doclipse)的创造者。

    TestNG是一个受JUnit和NUnit启发的测试框架,但引入了一些使其更强大且更易于使用的新功能,例如:

    • 注解: @Test、@BeforeXXX、@AfterXXX
    • 在具有各种可用策略的任意大线程池中运行测试(所有方法都在其自己的线程中,每个测试类一个线程,等等)。
    • 测试您的代码是多线程安全的。
    • 灵活的测试配置。
    • 支持数据驱动的测试(使用@DataProvider)。
    • 支持参数。
    • 强大的执行模型(不再需要TestSuite)。
    • 由各种工具和插件(Eclipse,IDEA,Maven等)支持。
    • 嵌入BeanShell以获得更高的灵活性。
    • 用于运行时和日志记录的默认JDK函数(无依赖项)。
    • 应用服务器测试的相关方法。
    • TestNG旨在涵盖所有类别的测试:单元测试,功能测试,端到端测试,集成测试等。

    编写测试通常分为三个步骤:

    • 编写测试的业务逻辑,然后在代码中插入TestNG注解
    • 在<tt>testng.xml</tt>文件或build.xml中添加有关测试的信息(例如,类名,您希望运行的组等)。
    • 运行TestNG

    二、常用@注解

    注解 描述
    @BeforeSuite 在测试套件运行开始执行,仅运行一次
    @AfterSuite 在测试套件运行结束执行,仅运行一次
    @BeforeClass 在测试类开始执行,每个类仅运行一次
    @AfterClass 在测试类结束后执行,每个类仅运行一次
    @BeforeTest 在测试用例开始执行,作用于testng.xml <test>标签,仅运行一次
    @AfterTest 在测试用例开始执行,作用于testng.xml <test>标签,仅运行一次
    @BeforeGroups 作用于定义的分组测试之前执行
    @AfterGroups 作用于定义的分组测试之后执行
    @BeforeMethod 在每个测试方法之前运行
    @AfterMethod 在每个测试方法之后运行
    @DataProvider 标记一种方法来提供测试方法的数据。 注解方法必须返回一个对象的数组,其中每个Object []可以被分配给测试方法的参数列表。
    @Factory @Factory"将一个方法标记为工厂,返回TestNG将被用作测试类的对象,方法必须返回一个对象数组
    @Listeners 定义测试类上的侦听器,如果不满足,可以自定义监听器
    @Parameters 描述如何将参数传递给@Test方法
    @Test 将类或方法标记为测试的一部分,最常用

    执行顺序:beforesuite->beforetest->beforeclass->beforeGroups->beforemethod->aftermethod->-afterGroups>afterclass->aftertest->aftersuite

    常用@注解实例

    1.配置(预置)类注解

    先创建一个TestConfig.java 文件

    package com.weidian.testNgDemo;
    
    import org.testng.annotations.*;
    
    public class TestConfig {
        @Test(groups = "order")
        public void test1() {
            System.out.println("testcase2");
        }
    
        @Test(groups = {"pay", "order"})
        public void test2() {
            System.out.println("testcase2");
        }
    
        @BeforeGroups("order")
        public void beforeGroup() {
            System.out.println("beforeGroup");
        }
    
        @AfterGroups("order")
        public void afterGroup() {
            System.out.println("afterGroup");
        }
    
        @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");
        }
    
        @BeforeTest
        public void beforeTest() {
            System.out.println("beforeTest");
        }
    
        @AfterTest
        public void afterTest() {
            System.out.println("afterTest");
        }
    
        @BeforeSuite
        public void beforeSuite() {
            System.out.println("beforeSuite");
        }
    
        @AfterSuite
        public void afterSuite() {
            System.out.println("afterSuite");
        }
    
    }
    
    

    结果输出:

    beforeSuite
    beforeTest
    beforeClass
    beforeGroup
    beforeMethod
    testcase2
    afterMethod
    afterGroup
    afterClass
    afterTest
    afterSuite
    
    ===============================================
    Default Suite
    Total tests run: 1, Failures: 0, Skips: 0
    ===============================================
    Process finished with exit code 0
    
    2.分组注解

    TestNG允许您对测试方法进行复杂的分组。您不仅可以声明方法属于组,还可以指定包含其他组的组。然后可以调用TestNG并要求它包括一组特定的组(或正则表达式),而排除另一组。如果要背对背运行两组不同的测试,这为您提供了最大的灵活性来划分测试的方式,并且不需要重新编译任何内容。

    组在您的testng.xml文件中指定,可以在<test>或<suite>标记下找到。<suite>标记中指定的组适用于下面的所有<test>标记。请注意,组在这些标记中是累积的:如果在<suite>中指定组“ a”,在<test>中指定组“ b” ,则将同时包括“ a”和“ b”。

    • Check-in tests: 在提交新代码之前,应先运行这些测试。它们通常应该很快,并且只需确保没有破坏任何基本功能即可。

    • Functional tests: 这些测试应涵盖软件的所有功能,并且每天至少要运行一次,尽管理想情况下,您希望连续运行它们。
      通常,Check-in tests是Functional tests的子集。TestNG允许您通过测试组以非常直观的方式指定此名称。例如,您可以通过说整个测试类都属于“ functest”组来构造您的测试:

    package com.weidian.testNgDemo;
    
    import org.testng.annotations.AfterGroups;
    import org.testng.annotations.BeforeGroups;
    import org.testng.annotations.Test;
    
    public class TestGroups {
        @Test(groups = "order")
        public void test1() {
            System.out.println("testcase1属于【order】分组");
        }
    
        @Test(groups = "pay")
        public void test2() {
            System.out.println("testcase2属于【pay】分组");
        }
    
        @Test(groups = {"pay", "order"})
        public void test3() {
            System.out.println("testcase3属于【order】【pay】分组");
        }
    
        @BeforeGroups("order")
        public void beforeGroup() {
            System.out.println("beforeGroup Start............");
        }
    
        @AfterGroups("order")
        public void afterGroup() {
            System.out.println("afterGroup END............");
        }
    }
    

    xml文件描述配置:将运行该类中的所有测试方法,而假设如果只使用order调用仅将运行 test1()和test3()

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
    <suite name="TestGroupSuite">
        <test name="GroupTest">
            <groups>
                <run>
                    <include name="order"/>  
                    <include name="pay"/>
                </run>
            </groups>
            <classes>
                <class name="com.weidian.testNgDemo.TestGroups"/>
            </classes>
        </test>
    </suite>
    

    使用排除组方式:

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
    <suite name="TestGroupSuite">
        <test name="GroupTest">
            <groups>
                <run>
                    <include name="order"/>  
                    <exclude name="pay"/>  !-- 将排除这个组的调用
                </run>
            </groups>
            <classes>
                <class name="com.weidian.testNgDemo.TestGroups"/>
            </classes>
        </test>
    </suite>
    
    3.异常测试

    如果你期望被测代码抛出一个指定异常,那么需要在@test 加上expectedExceptions=ArithmeticException

    package com.weidian.testNgDemo;
    
    import org.testng.annotations.Test;
    
    public class ExceptionTest {
        @Test(expectedExceptions = ArithmeticException.class)  //指定了异常pass
        public void testException() {
            int num = 3 / 0;
        }
    
        //抛出异常Test成功
        @Test(expectedExceptions = NumberFormatException.class)  //未指定对应的异常,fail
        public void testException2() {
            int num = 3 / 0;
        }
    }
    

    以上结果输出:

    org.testng.TestException: 
    Expected exception of type class java.lang.NumberFormatException but got java.lang.ArithmeticException: / by zero
    
    
    ===============================================
    Default Suite
    Total tests run: 2, Failures: 1, Skips: 0
    ===============================================
    
    Process finished with exit code 0
    
    4.忽略测试

    TestNG让您忽略所有@Test方法,使用新的注释@Ignore,在方法级别使用@Ignore注释在功能上等效于@Test(enabled = false)。这是一个示例,显示了如何忽略类中的所有测试:

    
    @Ignore  //**注意**如果在包处使用该注解,那么该包及其所有子包中忽略所有@Test方法
    package com.weidian.testNgDemo;
    
    import org.testng.annotations.Ignore;
    import org.testng.annotations.Test;
    
    @Ignore  //@Ignore 注解具有比个人更高的优先级@Test方法的注释。将@Ignore放在一个类上时,该类中的所有测试都将被禁用
    public class IgnoreTest {
        @Test(enabled = false)
        public void testMethod1() {
            System.out.println("Ignore Test1.");
        }
    
        @Ignore  //@Ignore注释在功能上等效于@Test(enabled = false)
        @Test
        public void testMethod2() {
            System.out.println("Ignore Test2.");
        }
    }
    
    ===============================================
    Default Suite
    Total tests run: 0, Failures: 0, Skips: 0
    ===============================================
    Process finished with exit code 0
    
    5.超时测试

    “超时”表示如果单元测试花费的时间超过指定的毫秒数,那么TestNG将会中止它并将其标记为失败:

    package com.weidian.testNgDemo;
    
    import org.testng.annotations.Test;
    
    public class TimeOutTest {
        @Test(timeOut = 5000)
        public void testPass() throws InterruptedException {
            Thread.sleep(4000);
        }
    
        @Test(timeOut = 1000)
        public void testFail() throws InterruptedException {
            Thread.sleep(2000);
        }
    }
    
    org.testng.internal.thread.ThreadTimeoutException:
     Method com.weidian.testNgDemo.TimeOutTest.testThisShouldFail() didn't finish within the time-out 1000
    
    ===============================================
    Default Suite
    Total tests run: 2, Failures: 1, Skips: 0
    ===============================================
    
    Process finished with exit code 0
    

    同时,如果想测试异步操作,也可以使用timeout。可以通过设置timeout超时时间、invocationCount调用次数、invocationTimeout调用超时总时间来测试异步操作,比如导出或者离线下载一个大一点的报表文件,前段请求可能是轮询多次进行的,那么我们也可以来模拟一下

    package com.weidian.testNgDemo;
    
    import org.testng.annotations.Test;
    
    public class TimeOutTest {
        @Test(invocationCount = 3, invocationTimeOut = 5000)
        public void testInvocation() throws InterruptedException {
            System.out.println("轮询下载文件");
            Thread.sleep(2000);
        }
    }
    
    轮询下载文件
    轮询下载文件
    轮询下载文件
    
    org.testng.internal.thread.ThreadTimeoutException: Method com.weidian.testNgDemo.TimeOutTest.testInvocation() didn't finish within the time-out 5000
    
    ===============================================
    Default Suite
    Total tests run: 1, Failures: 1, Skips: 0
    ===============================================
    
    Process finished with exit code 0
    
    
    5.依赖测试

    在做单元测试的过程中,假设需要测试多个方法之间的关联关系之间的场景,那么就需要用到@Test(dependsOnMethods=XXX)这个属性了。如果依赖的上一个测试用例失败,那么接下来的将会skip:

    package com.weidian.testNgDemo;
    
    import org.testng.Assert;
    import org.testng.annotations.Ignore;
    import org.testng.annotations.Test;
    
    public class DependsOnMethodsTest {
        @Test
        public void method1() {
            System.out.println("This is method 1");
        }
    
        @Test(dependsOnMethods = { "method1" })
        public void method2() {
            System.out.println("depends On method 2");
            Assert.assertEquals(1, 0);  // 这条用例失败,那么后面的将不会执行.
        }
    
        @Test(dependsOnMethods = { "method2" })
        public void method3() {
            System.out.println("depends On method 3");
        }
    }
    
    This is method 1
    depends On method 2
    
    java.lang.AssertionError: expected [0] but found [1]
    Expected :0
    Actual   :1
    
    Test ignored.
    ===============================================
    Default Suite
    Total tests run: 3, Failures: 1, Skips: 1
    ===============================================
    
    Process finished with exit code 0
    

    当然这是方法依赖的使用,组依赖可以使用 dependsOnGroups,并且两种方式可以混合使用,都一样的使用方式,这里就不一一举例了,有兴趣的小伙伴可以自行去尝试一下,也可以去参考一下官方文档。

    6.多线程负载测试

    threadPoolSize 属性告诉TestNG创建一个线程池以通过多个线程运行测试方法。 使用线程池,会大大降低测试方法的运行时间,invocationCount 确定TestNG应该运行这个测试方法的次数。

    package com.weidian.testNgDemo;
    
    import org.testng.annotations.Test;
    
    public class ThreadPoolTest {
    
        @Test(invocationCount = 10, threadPoolSize = 5)
        public void testInvocation() {
            System.out.println("threadId:" + Thread.currentThread().getId()+"-->轮询下载文件");    }
    }
    
    threadId:16-->轮询下载文件
    threadId:14-->轮询下载文件
    threadId:13-->轮询下载文件
    threadId:15-->轮询下载文件
    threadId:12-->轮询下载文件
    threadId:16-->轮询下载文件
    threadId:15-->轮询下载文件
    threadId:12-->轮询下载文件
    threadId:13-->轮询下载文件
    threadId:14-->轮询下载文件
    
    ===============================================
    Default Suite
    Total tests run: 10, Failures: 0, Skips: 0
    ===============================================
    Process finished with exit code 0
    
    7.参数化测试

    TestNG 中的另一个有趣的功能是参数化测试。 在大多数情况下,您会遇到业务逻辑需要大量测试的场景。 参数化测试允许开发人员使用不同的值一次又一次地运行相同的测试。
    TestNG可以通过两种不同的方式将参数直接传递给测试方法:

    • XML 使用@Parameters进行参数化配置
    package com.weidian.testNgDemo;
    
    import org.testng.annotations.Parameters;
    import org.testng.annotations.Test;
    
    public class DataProviderTest {
        @Parameters({ "name", "age" })
        @Test
        public void person(String name, int age) {
            System.out.println("姓名:"+name+", 年龄:"+age);
        }
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <suite name="DataProviderTest">
        <test name="DataProviderTest">
            <parameter name="name" value="古力娜扎" />
            <parameter name="age" value="18" />
    
            <classes>
                <class name="com.weidian.testNgDemo.DataProviderTest" />
            </classes>
        </test>
    </suite>
    
    

    注意:需要去执行testng.xml文件,而不是直接在idea里面执行方法或者类,否则将抛出: [Error] org.testng.TestNGException: Parameter &apos;name&apos; is required by @Test on method person but has not been marked @Optional or defined

    姓名:古力娜扎, 年龄:18
    
    ===============================================
    DataProviderTest
    Total tests run: 1, Failures: 0, Skips: 0
    ===============================================
    
    Process finished with exit code 0
    
    • @DataProvider将参数传递给@Test方法
    package com.weidian.testNgDemo;
    
    import org.testng.annotations.DataProvider;
    import org.testng.annotations.Test;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Properties;
    
    public class DataProviderTest {
        @Test(dataProvider = "dbConfig")
        public void dataProviderTest(Map<String, String> map) {
            for (Map.Entry<String, String> entry : map.entrySet()) {
                System.out.println("[Key] : " + entry.getKey() + " [Value] : " + entry.getValue());
            }
    
        }
    
        @DataProvider(name = "dbConfig")
        public Object[][] dataProvideConfig() {
            Map<String, String> map = readDbConfig();
            return new Object[][] { { map } };
        }
    
        public Map<String, String> readDbConfig() {
    
            Properties prop = new Properties();
            InputStream input = null;
            Map<String, String> map = new HashMap<String, String>();
    
            try {
                input = getClass().getClassLoader().getResourceAsStream("db.properties");
                prop.load(input);
    
                map.put("jdbc.driver", prop.getProperty("jdbc.driver"));
                map.put("jdbc.url", prop.getProperty("jdbc.url"));
                map.put("jdbc.username", prop.getProperty("jdbc.username"));
                map.put("jdbc.password", prop.getProperty("jdbc.password"));
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (input != null) {
                    try {
                        ((InputStream) input).close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return map;
        }
    }
    
    "C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Testplan\IntelliJ IDEA 2020.1\lib\idea_rt.jar=50030:D:\Testplan\IntelliJ IDEA 2020.1\bin" -Dfile.encoding=UTF-8 -classpath "D:\Testplan\IntelliJ IDEA 2020.1\lib\idea_rt.jar;D:\Testplan\IntelliJ IDEA 2020.1\plugins\testng\lib\testng-rt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\rt.jar;C:\Users\Administrator\IdeaProjects\basic_code\out\production\day04-code;C:\Users\Administrator\.m2\repository\org\testng\testng\6.14.3\testng-6.14.3.jar;C:\Users\Administrator\.m2\repository\com\beust\jcommander\1.72\jcommander-1.72.jar;C:\Users\Administrator\.m2\repository\org\apache-extras\beanshell\bsh\2.0b6\bsh-2.0b6.jar;D:\Testplan\IntelliJ IDEA 2020.1\plugins\testng\lib\jcommander-1.27.jar" com.intellij.rt.testng.RemoteTestNGStarter -usedefaultlisteners false -socket50029 @w@C:\Users\Administrator\AppData\Local\Temp\idea_working_dirs_testng.tmp -temp C:\Users\Administrator\AppData\Local\Temp\idea_testng.tmp
    
    
    
    [Key] : jdbc.password [Value] : 123456
    [Key] : jdbc.username [Value] : root
    [Key] : jdbc.url [Value] : jdbc:mysql://localhost:3306/test
    [Key] : jdbc.driver [Value] : com.mysql.jdbc.Driver
    
    ===============================================
    Default Suite
    Total tests run: 1, Failures: 0, Skips: 0
    ===============================================
    
    Process finished with exit code 0
    

    当然这里只简单举例描述了读取配置文件的参数传递,实际过程中,可能使用的测试数据会管理在数据库、YAML、Json、Excel中,需要去单独封装一个方法传递给@Test(dataProvider = "dbConfig")即可,我实际过程中使用的是从Excel 中读取的。那种方式都可以,看个人习惯。

    8.重新运行失败的测试

    每当套件中的测试失败时,TestNG都会在输出目录中创建一个名为testng-failed.xml的文件。该XML文件包含必要的信息,以仅重新运行失败的这些方法,从而使您可以快速重现失败,而不必运行整个测试。因此,典型的会话如下所示:

    java -classpath testng.jar;%CLASSPATH% org.testng.TestNG -d test-outputs testng.xml
    java -classpath testng.jar;%CLASSPATH% org.testng.TestNG -d test-outputs test-outputs\testng-failed.xml
    请注意:testng-failed.xml将包含所有必需的依赖方法,因此可以确保您运行失败的方法而不会出现任何SKIP失败

    使用重试分析器的方法:

    • 构建接口org.testng.IRetryAnalyzer的实现将此实现
    • 绑定到@Test注释,例如@Test(retryAnalyzer = LocalRetry.class)

    以下是重写IRetryAnalyzer的示例实现,尝试了三次失败忽略:

    package com.weidian.testNgDemo;
    
    import org.testng.IRetryAnalyzer;
    import org.testng.ITestResult;
    
    public class MyRetry implements IRetryAnalyzer {
        private int retryCount = 0;
        private static final int maxRetryCount = 3;  // 设置默认重试次数为3
    
        @Override
        public boolean retry(ITestResult result) {
            if (retryCount < maxRetryCount) {
                retryCount++;
                return true;
            }
            return false;
        }
    }
    
    package com.weidian.testNgDemo;
    
    import org.testng.Assert;
    import org.testng.annotations.Test;
    
    public class RetryAnalyzerTest {
        @Test(retryAnalyzer = MyRetry.class)
        public void test2() {
            Assert.assertEquals(1, 0);
        }
    }
    
    "C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Testplan\IntelliJ IDEA 2020.1\lib\idea_rt.jar=52109:D:\Testplan\IntelliJ IDEA 2020.1\bin" -Dfile.encoding=UTF-8 -classpath "D:\Testplan\IntelliJ IDEA 2020.1\lib\idea_rt.jar;D:\Testplan\IntelliJ IDEA 2020.1\plugins\testng\lib\testng-rt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\rt.jar;C:\Users\Administrator\IdeaProjects\basic_code\out\production\day04-code;C:\Users\Administrator\.m2\repository\org\testng\testng\6.14.3\testng-6.14.3.jar;C:\Users\Administrator\.m2\repository\com\beust\jcommander\1.72\jcommander-1.72.jar;C:\Users\Administrator\.m2\repository\org\apache-extras\beanshell\bsh\2.0b6\bsh-2.0b6.jar;D:\Testplan\IntelliJ IDEA 2020.1\plugins\testng\lib\jcommander-1.27.jar" com.intellij.rt.testng.RemoteTestNGStarter -usedefaultlisteners false -socket52108 @w@C:\Users\Administrator\AppData\Local\Temp\idea_working_dirs_testng.tmp -temp C:\Users\Administrator\AppData\Local\Temp\idea_testng.tmp
    Test ignored.
    Test ignored.
    Test ignored.
    
    java.lang.AssertionError: expected [0] but found [1]
    Expected :0
    Actual   :1
    
    ===============================================
    Default Suite
    Total tests run: 4, Failures: 1, Skips: 3
    ===============================================
    
    Process finished with exit code 0
    

    三、监听器 Listener

    IInvokedMethodListener
    package org.testng;
    
    public interface IInvokedMethodListener extends ITestNGListener {
        void beforeInvocation(IInvokedMethod var1, ITestResult var2);
    
        void afterInvocation(IInvokedMethod var1, ITestResult var2);
    }
    

    IInvokedMethodListener接口继承自ITestNGListener接口,其定义了beforeInvocation和afterInvocation方法。TestNG在调用方法前、后启用该监听器,常用于日志的采集。举例如下:IInvokedMethodListenerImp实现类

    package com.weidian.testNgDemo;
    
    import org.testng.IInvokedMethod;
    import org.testng.IInvokedMethodListener;
    import org.testng.ITestResult;
    
    public class IInvokedMethodListenerImp implements IInvokedMethodListener {
    
        @Override
        public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
            System.out.println("监听之前打印方法名称:"+iTestResult.getName());
        }
    
        @Override
        public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
            System.out.println("监听之后输出方法名称:"+iTestResult.getName());
        }
    }
    
    
    package com.weidian.testNgDemo;
    
    import org.testng.annotations.BeforeClass;
    import org.testng.annotations.Listeners;
    import org.testng.annotations.Test;
    
    /**
     * 当这个类使用了@Listeners注解调用了自定义的的监听器之后
     * 下面的每个方法在运行前后都会触发IInvokedMethodListenerImp的beforeInvocation、afterInvocation方法
     */
    @Listeners(IInvokedMethodListenerImp.class)
    public class IInvokedMethodListenerImpTest {
    
        @BeforeClass
        public void beforeClass(){
            System.out.println("bfClass123");
        }
    
        @Test
        public void testMethod(){
            System.out.println("test123");
        }
    }
    
    "C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Testplan\IntelliJ IDEA 2020.1\lib\idea_rt.jar=56434:D:\Testplan\IntelliJ IDEA 2020.1\bin" -Dfile.encoding=UTF-8 -classpath "D:\Testplan\IntelliJ IDEA 2020.1\lib\idea_rt.jar;D:\Testplan\IntelliJ IDEA 2020.1\plugins\testng\lib\testng-rt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\rt.jar;C:\Users\Administrator\IdeaProjects\basic_code\out\production\day04-code;C:\Users\Administrator\.m2\repository\org\testng\testng\6.14.3\testng-6.14.3.jar;C:\Users\Administrator\.m2\repository\com\beust\jcommander\1.72\jcommander-1.72.jar;C:\Users\Administrator\.m2\repository\org\apache-extras\beanshell\bsh\2.0b6\bsh-2.0b6.jar;D:\Testplan\IntelliJ IDEA 2020.1\plugins\testng\lib\jcommander-1.27.jar" com.intellij.rt.testng.RemoteTestNGStarter -usedefaultlisteners false -socket56433 @w@C:\Users\Administrator\AppData\Local\Temp\idea_working_dirs_testng.tmp -temp C:\Users\Administrator\AppData\Local\Temp\idea_testng.tmp
    监听之前打印方法名称:beforeClass
    bfClass123
    监听之后输出方法名称:beforeClass
    
    监听之前打印方法名称:testMethod
    test123
    监听之后输出方法名称:testMethod
    ===============================================
    Default Suite
    Total tests run: 1, Failures: 0, Skips: 0
    ===============================================
    
    Process finished with exit code 0
    

    从上面的运行结果来看,所有被注解的方法执行前都先执行监听器的beforeInvocation方法,执行后都执行afterInvocation方法。
    另外,TestNG还提供了IInvokedMethodListener2监听器,据说其中定义的方法可以加入用户信息的参数,使用起来更灵活。有兴趣的小伙伴可以自行去尝试。

    package org.testng;
    
    public interface IInvokedMethodListener2 extends IInvokedMethodListener {
        void beforeInvocation(IInvokedMethod var1, ITestResult var2, ITestContext var3);
    
        void afterInvocation(IInvokedMethod var1, ITestResult var2, ITestContext var3);
    }
    

    这里只是抛砖引玉记录了一种监听器的使用,还有许多常用的监听器在使用testNG的过程使用比较频繁,感兴趣的可以去参考一下:

    TestNG学习之路—TestNG监听器
    TestNg的IReporter接口的使用
    实战 TestNG 监听器

    ·荣耀存于心 而非留于形 吾虽浪迹天涯 却未迷失本心 长路漫漫 惟剑做伴 且随疾风前行 身后亦需留心 吾之初心 永世不忘·--《lo疾风剑豪》

    相关文章

      网友评论

          本文标题:TestNG - 单元测试框架 quickStart

          本文链接:https://www.haomeiwen.com/subject/tpvodktx.html