• TestNG与ExtentReport集成


    TestNG与ExtentReport集成

    2020-03-10

    目录

    1 通过实现ITestListener的方法添加Reporter log
      1.1 MyTestListener设置
      1.2 输出结果
    2 TestNG与ExtentReporter集成
      2.1 项目结构
      2.2 MyExtentReportListener设置
      2.3 单多Suite、Test组合测试
        2.3.1 单Suite单Test
        2.3.2 单Suite多Test
        2.3.3 多Suite

     源代码:interface-test-framework.zip

    1 通过实现ITestListener的方法添加Reporter log


     返回

    TestNG的Listener列表

    TestNG提供了一组预定义的Listener Java接口,这些接口全部继承自TestNG的 ITestNGListener接口。用户创建这些接口的实现类,并把它们加入到 TestNG 中,TestNG便会在测试运行的不同时刻调用这些类中的接口方法:

    • IExecutionListener   监听TestNG运行的启动和停止。
    • IAnnotationTransformer 注解转换器,用于TestNG测试类中的注解。
    • ISuiteListener 测试套件监听器,监听测试套件的启动和停止。
    • ITestListener  测试方法执行监听。
    • IConfigurationListener 监听配置方法相关的接口。
    • IMethodInterceptor 拦截器,调整测试方法的执行顺序。
    • IInvokedMethodListener 测试方法拦截监听,用于获取被TestNG调用的在Method的Before 和After方法监听器。该方法只会被配置和测试方法调用。
    • IHookable 若测试类实现了该接口,当@Test方法被发现时,它的run()方法将会被调用来替代@Test方法。这个测试方法通常在IHookCallBack的callback之上调用,比较适用于需要JASS授权的测试类。
    • IReporter 实现该接口可以生成一份测试报告。

    本文将着重介绍最常用到的两个Listener ITestListener与Ireporter接口。

    1.1 MyTestListener设置

    通过实现ITestListener的方法,添加Reporter.log

    MyTestListener.java

    import org.testng.ITestContext;
    import org.testng.ITestListener;
    import org.testng.ITestResult;
    import org.testng.Reporter;
    
    public class MyTestListener implements ITestListener {
        //用例执行结束后,用例执行成功时调用
        public void onTestSuccess(ITestResult tr) {
            logTestEnd(tr, "Success");
        }
    
        //用例执行结束后,用例执行失败时调用
        public void onTestFailure(ITestResult tr) {
            logTestEnd(tr, "Failed");
        }
    
        //用例执行结束后,用例执行skip时调用
        public void onTestSkipped(ITestResult tr) {
            logTestEnd(tr, "Skipped");
        }
    
        //每次方法失败但是已经使用successPercentage进行注释时调用,并且此失败仍保留在请求的成功百分比之内。
        public void onTestFailedButWithinSuccessPercentage(ITestResult tr) {
            logTestEnd(tr, "FailedButWithinSuccessPercentage");
        }
    
        //每次调用测试@Test之前调用
        public void onTestStart(ITestResult result) {
            logTestStart(result);
        }
    
        //在测试类被实例化之后调用,并在调用任何配置方法之前调用。
        public void onStart(ITestContext context) {
            return;
        }
    
        //在所有测试运行之后调用,并且所有的配置方法都被调用
        public void onFinish(ITestContext context) {
            return;
        }
    
        // 在用例执行结束时,打印用例的执行结果信息
        protected void logTestEnd(ITestResult tr, String result) {
            Reporter.log(String.format("-------------Result: %s-------------", result), true);
        }
    
        // 在用例开始时,打印用例的一些信息,比如@Test对应的方法名,用例的描述等等
        protected void logTestStart(ITestResult tr) {
            Reporter.log(String.format("-------------Run: %s.%s---------------", tr.getTestClass().getName(), tr.getMethod().getMethodName()), true);
            Reporter.log(String.format("用例描述: %s, 优先级: %s", tr.getMethod().getDescription(), tr.getMethod().getPriority()),true);
            return;
        }
    }
    View Code

    1.2 输出结果

    SmkDemo1.java

    import com.demo.listener.MyTestListener;
    import org.testng.Assert;
    import org.testng.Reporter;
    import org.testng.annotations.Listeners;
    import org.testng.annotations.Test;
    
    @Listeners({MyTestListener.class})
    public class SmkDemo1 {
        @Test(description="测testPass11的描述",priority = 1,groups = {"分组1"})
        public void testPass11(){
            Reporter.log("Test11的第一步",true);
            Assert.assertEquals(1,1);
        }
    }

    输出界面显示如下

    图1 log to 输出界面

    2 TestNG与ExtentReporter集成


     返回

    2.1 项目结构

     

    图2 项目结构

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.demo</groupId>
        <artifactId>interface-test-framework</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <!-- 测试报告插件和testng的结合 -->
            <dependency>
                <groupId>com.vimalselvam</groupId>
                <artifactId>testng-extentsreport</artifactId>
                <version>1.3.1</version>
            </dependency>
            <!-- extentreports测试报告插件 -->
            <dependency>
                <groupId>com.aventstack</groupId>
                <artifactId>extentreports</artifactId>
                <version>3.0.6</version>
            </dependency>
            <dependency>
                <groupId>org.testng</groupId>
                <artifactId>testng</artifactId>
                <version>7.1.0</version>
            </dependency>
        </dependencies>
    </project>
    View Code

    2.2 MyExtentReportListener设置

    MyExtentReporterListener.java

    import com.aventstack.extentreports.ExtentReports;
    import com.aventstack.extentreports.ExtentTest;
    import com.aventstack.extentreports.ResourceCDN;
    import com.aventstack.extentreports.Status;
    import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
    import com.aventstack.extentreports.reporter.configuration.ChartLocation;
    import com.aventstack.extentreports.reporter.configuration.Theme;
    import org.testng.*;
    import org.testng.xml.XmlSuite;
    
    import java.io.File;
    import java.util.*;
    
    public class MyExtentReporterListener implements IReporter {
        //生成的路径以及文件名
        private static final String OUTPUT_FOLDER = "test-output/";
        private static final String FILE_NAME = "report.html";
    
        private ExtentReports extent;
    
        public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
            init();
            boolean createSuiteNode = false;
            if(suites.size()>1){
                createSuiteNode=true;
            }
            for (ISuite suite : suites) {
                Map<String, ISuiteResult> result = suite.getResults();
                //如果suite里面没有任何用例,直接跳过,不在报告里生成
                if(result.size()==0){
                    continue;
                }
                //统计suite下的成功、失败、跳过的总用例数
                int suiteFailSize=0;
                int suitePassSize=0;
                int suiteSkipSize=0;
                ExtentTest suiteTest=null;
                //存在多个suite的情况下,在报告中将同一个一个suite的测试结果归为一类,创建一级节点。
                if(createSuiteNode){
    //                suiteTest = extent.createTest(suite.getName()).assignCategory(suite.getName());
                    suiteTest = extent.createTest(suite.getName());
                }
                boolean createSuiteResultNode = false;
                if(result.size()>1){
                    createSuiteResultNode=true;
                }
                for (ISuiteResult r : result.values()) {
                    ExtentTest resultNode=null;
                    ITestContext context = r.getTestContext();
                    if(createSuiteResultNode){
                        //没有创建suite的情况下,将在SuiteResult的创建为一级节点,否则创建为suite的一个子节点。
                        if( null == suiteTest){
                            resultNode = extent.createTest(r.getTestContext().getName());
                        }else{
                            resultNode = suiteTest.createNode(r.getTestContext().getName());
                        }
                    }else{
                        resultNode = suiteTest;
                    }
                    String[] categories=new String[1];
                    if(resultNode != null){
                        resultNode.getModel().setName(suite.getName()+"."+r.getTestContext().getName());
                        if(resultNode.getModel().hasCategory()){
                            resultNode.assignCategory(r.getTestContext().getName());
                        }else{
    //                        resultNode.assignCategory(suite.getName(),r.getTestContext().getName());
                            categories[0]=suite.getName()+"."+r.getTestContext().getName();
                        }
                        resultNode.getModel().setStartTime(r.getTestContext().getStartDate());
                        resultNode.getModel().setEndTime(r.getTestContext().getEndDate());
                        //统计SuiteResult下的数据
                        int passSize = r.getTestContext().getPassedTests().size();
                        int failSize = r.getTestContext().getFailedTests().size();
                        int skipSize = r.getTestContext().getSkippedTests().size();
                        suitePassSize += passSize;
                        suiteFailSize += failSize;
                        suiteSkipSize += skipSize;
                        if(failSize>0){
                            resultNode.getModel().setStatus(Status.FAIL);
                        }
                        resultNode.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",passSize,failSize,skipSize));
                    }
                    buildTestNodes(resultNode,categories,context.getFailedTests(), Status.FAIL);
                    buildTestNodes(resultNode,categories,context.getSkippedTests(), Status.SKIP);
                    buildTestNodes(resultNode,categories,context.getPassedTests(), Status.PASS);
    
    
                }
                if(suiteTest!= null){
                    suiteTest.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",suitePassSize,suiteFailSize,suiteSkipSize));
                    if(suiteFailSize>0){
                        suiteTest.getModel().setStatus(Status.FAIL);
                    }
                }
    
            }
    //        for (String s : Reporter.getOutput()) {
    //            extent.setTestRunnerOutput(s);
    //        }
    
            extent.flush();
        }
    
        private void init() {
            //文件夹不存在的话进行创建
            File reportDir= new File(OUTPUT_FOLDER);
            if(!reportDir.exists()&& !reportDir .isDirectory()){
                reportDir.mkdir();
            }
            ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);
            // 设置静态文件的DNS
            //解决cdn访问不了的问题
            htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);
    
            htmlReporter.config().setDocumentTitle("api自动化测试报告");
            htmlReporter.config().setReportName("api自动化测试报告");
            htmlReporter.config().setChartVisibilityOnOpen(true);
            htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
            htmlReporter.config().setTheme(Theme.STANDARD);
            htmlReporter.config().setCSS(".node.level-1  ul{ display:none;} .node.level-1.active ul{display:block;}");
            extent = new ExtentReports();
            extent.attachReporter(htmlReporter);
            extent.setReportUsesManualConfiguration(true);
        }
    
        private void buildTestNodes(ExtentTest extenttest, String[] categories, IResultMap tests, Status status) {
    //        //存在父节点时,获取父节点的标签
    //        String[] categories=new String[0];
    //        if(extenttest != null ){
    //            List<TestAttribute> categoryList = extenttest.getModel().getCategoryContext().getAll();
    //            categories = new String[categoryList.size()];
    //            for(int index=0;index<categoryList.size();index++){
    //                categories[index] = categoryList.get(index).getName();
    //            }
    //        }
    
            ExtentTest test;
    
            if (tests.size() > 0) {
                //调整用例排序,按时间排序
                Set<ITestResult> treeSet = new TreeSet<ITestResult>(new Comparator<ITestResult>() {
                    public int compare(ITestResult o1, ITestResult o2) {
                        return o1.getStartMillis()<o2.getStartMillis()?-1:1;
                    }
                });
                treeSet.addAll(tests.getAllResults());
                for (ITestResult result : treeSet) {
                    Object[] parameters = result.getParameters();
                    String name="";
                    //如果有参数,则使用参数的toString组合代替报告中的name
                    for(Object param:parameters){
                        name+=param.toString();
                    }
                    if(name.length()>0){
                        if(name.length()>50){
                            name= name.substring(0,49)+"...";
                        }
                    }else{
                        name = result.getMethod().getMethodName();
                    }
                    if(extenttest==null){
                        test = extent.createTest(name);
                    }else{
                        //作为子节点进行创建时,设置同父节点的标签一致,便于报告检索。
                        test = extenttest.createNode(name).assignCategory(categories);
                    }
                    //test.getModel().setDescription(description.toString());
                    //test = extent.createTest(result.getMethod().getMethodName());
                    for (String group : result.getMethod().getGroups())
                        test.assignCategory(group);
    
                    List<String> outputList = Reporter.getOutput(result);
                    for(String output:outputList){
                        //将用例的log输出报告中
                        test.debug(output);
                    }
                    if (result.getThrowable() != null) {
                        test.log(status, result.getThrowable());
                    }
                    else {
                        test.log(status, "Test " + status.toString().toLowerCase() + "ed");
                    }
    
                    test.getModel().setStartTime(getTime(result.getStartMillis()));
                    test.getModel().setEndTime(getTime(result.getEndMillis()));
                }
            }
        }
    
        private Date getTime(long millis) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(millis);
            return calendar.getTime();
        }
    }
    View Code

    2.3 单多Suite、Test组合测试

    2.3.1 单Suite单Test

    testngSingleSuiteSingleTest.xml

    <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
    <suite name="SingleSuite">
        <test name="SingleTest" verbose="1" preserve-order="true" >
            <classes>
                <class name="com.demo.testcase.smoke.SmkDemo1">
                </class>
                <class name="com.demo.testcase.sit.SitDemo2">
                </class>
            </classes>
        </test>
        <!--配置监听器-->
        <listeners>
            <listener class-name="com.demo.listener.MyTestListener"/>
            <listener class-name="com.demo.listener.MyExtentReporterListener"/>
        </listeners>
    </suite>
    View Code

     图3 单Suite单Test总览

     

      图4 单Suite单Test分组

       图5 单Suite单Test错误分组

       图6 单Suite单Test Dashboard

    2.3.2 单Suite多Test

    testngSingleSuiteDoubleTest.xml

    <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
    <suite name="SingleSuite">
        <test name="DoubleTest1" verbose="1" preserve-order="true" >
            <classes>
                <class name="com.demo.testcase.smoke.SmkDemo1">
                </class>
            </classes>
        </test>
        <test name="DoubleTest2" verbose="1" preserve-order="true" >
            <classes>
                <class name="com.demo.testcase.sit.SitDemo2">
                </class>
            </classes>
        </test>
        <!--配置监听器-->
        <listeners>
            <listener class-name="com.demo.listener.MyTestListener"/>
            <listener class-name="com.demo.listener.MyExtentReporterListener"/>
        </listeners>
    </suite>
    View Code

     

     图7 单Suite多Test总览

     图8 单Suite多Test分组

    2.3.3 多Suite

    testngDoubleSuite.xml

    <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
    <suite name="DoubleSuite">
        <suite-files>
            <suite-file path="testngSingleSuiteSingleTest.xml"/>
            <suite-file path="testngSingleSuiteDoubleTest.xml"/>
        </suite-files>
        <!--配置监听器-->
        <listeners>
            <listener class-name="com.demo.listener.MyTestListener"/>
            <listener class-name="com.demo.listener.MyExtentReporterListener"/>
        </listeners>
    </suite>
    View Code

     图9 多Suite总览1

     图10 多Suite总览2

     图11 多Suite分组

    参考

    [1] testng框架Listener介绍及测试结果的收集 

    [2] TestNG执行的日志ITestListener与结果IReporter

    [3] TestNG执行的日志ITestListener与结果IReporter

    [4] TestNg Beginner's Guide--阅后总结之Textng.xml

    [5] TestNg Beginner's Guide--阅后总结之TestNg注解

  • 相关阅读:
    网络协议-应用层协议-HTTP协议简介
    网络协议-应用层协议-电子邮件协议
    网络协议-应用层协议-文件传输协议
    网络协议-应用层协议-远程登陆协议
    网络协议-应用层协议-概述
    网络协议-传输层协议-Socket编程
    网络协议-传输层协议-TCP协议
    网络协议-传输层协议-UDP协议
    北华大学网络赛题
    哈尔滨网络热身赛
  • 原文地址:https://www.cnblogs.com/Ming8006/p/12457365.html
Copyright © 2020-2023  润新知