测试报告框架ExtentReports 的简单使用(上)

发表于:2023-1-17 09:25

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:雨燕    来源:博客园

  1. 简介
  简单来说 ExtentReports 就是一个测试报告框架,可以简单的应用到我们日常的单元测试接口测试等中。同时它还提供了 测试报告服务器 klov。
  官网上有这段话:
  ExtentReports is an logger-style reporting library for automated tests. A logger is simply an object to log messages or events for a specific system or application. ExtentReports uses the logging style to add information about test sessions, such as creation of tests, adding screenshots, assigning tags, and adding events or series of steps to sequentially indicate the flow of test steps.
  翻译过来:
  ExtentReports 是一个用于生成自动化测试报告的库。报告中会详细记录系统或应用程序的消息或事件对象。ExtentReports 可以添加有关测试会话的信息,例如创建测试、添加屏幕截图、分配标记,以及添加事件或一系列步骤,以顺序指示测试步骤的流程。支持两种语言JAVA,NET,ExtentReports有个很重要的组件 KLOV报表服务器,它提供了对历史报告的数据分析。
  客户端:https://github.com/extent-framework/extentreports-java, DOC
  服务端:https://github.com/extent-framework/klov,Doc

  2. ExtentReports java 实现
  2.1 在pom.xml文件中配置依赖
        <dependency>
            <groupId>com.aventstack</groupId>
            <artifactId>extentreports</artifactId>
            <version>5.0.8</version>
        </dependency>

  2.2 在工程目录下添加两个类
  监听类 ExtentTestNGIReporterListener 用于实现 testng 的 IReporter 接口,ExtentManager 用于生成 ExtentReports。
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.Status;
import org.testng.*;
import org.testng.xml.XmlSuite;

import java.util.*;

/**
 * @author nan
 * @title: ExtentTestertNgFormat
 * @projectName ISTP_new_dev
 * @description: TODO
 * @date 2021/11/249:42
 */

public class ExtentTestNGIReporterListener implements IReporter {
    private ExtentReports extent = ExtentManager.getInstance();

    @Override
    public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
        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());
            }
            boolean createSuiteResultNode = false;
            if (result.size() > 1) {
                createSuiteResultNode = true;
            }
            for (ISuiteResult r : result.values()) {
                ExtentTest resultNode;
                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;
                }
                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());
                    }
                    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, context.getFailedTests(), Status.FAIL);
                buildTestNodes(resultNode, context.getSkippedTests(), Status.SKIP);
                buildTestNodes(resultNode, 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);
                }
            }

        }
        extent.flush();
    }



    private void buildTestNodes(ExtentTest extenttest, 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>() {
                @Override
                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);
                }
                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();
    }
}

import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.reporter.ExtentKlovReporter;
import com.aventstack.extentreports.reporter.ExtentSparkReporter;
import com.aventstack.extentreports.reporter.configuration.ExtentSparkReporterConfig;
import com.aventstack.extentreports.reporter.configuration.Theme;

import java.io.File;

/**
 * @author nan
 * @title: ExtentManager
 * @projectName ISTP_new_dev
 * @description: TODO
 * @date 2021/11/2510:51
 */
public class ExtentManager {
    //生成的路径以及文件名
    private static final String OUTPUT_FOLDER = "test-output/";
    private static final String FILE_NAME = "index5.html";
    private static ExtentReports extent;

    public static ExtentReports getInstance() {
        if (extent == null)
            createInstance();
        return extent;
    }

    public static void createInstance() {
        //文件夹不存在的话进行创建
        File reportDir = new File(OUTPUT_FOLDER);
        if (!reportDir.exists() && !reportDir.isDirectory()) {
            reportDir.mkdir();
        }
        extent = new ExtentReports();
        extent.attachReporter(createSparkReporter(OUTPUT_FOLDER + FILE_NAME));
        //extent.setSystemInfo("os", "Linux");
        extent.setReportUsesManualConfiguration(true);
    }

    private static ExtentSparkReporter createSparkReporter(String filePath) {
        ExtentSparkReporter spark = new ExtentSparkReporter(filePath);
        spark.config(
                ExtentSparkReporterConfig.builder()
                        .theme(Theme.DARK)
                        .reportName("ISTP API自动化测试报告")
                        .documentTitle("ISTP API自动化测试报告")
                        .build());
        return spark;
    }
}

  2.3 在测试执行xml文件中配置监听
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="这是我自己的接口测试套件">
    <test name="这是测试模块">
        <classes>
            <class name="com.report.ReportTest"/>
            <methods>
......
            </methods>
        </classes>
    </test>
    <listeners>
    <!--这是配置生成报告的监听-->
        <listener class-name="com..report.ExtentTestNGIReporterListener"/>
    </listeners>
</suite>

  2.4 运行测试,并查看报告
  在报告输出路径下可以看到已生成的测试报告,详见下图。

  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号