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注解

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM