testng自定义html报告,根据freemaker生成

【转】

https://testerhome.com/topics/3487

【参考】https://www.cnblogs.com/cheese320/p/8890929.html  做了些修改,换了模板引擎,换为freemaker。

在test用例中增加注解,或者在testng.xml中添加监听器配置

//<listener class-name="report.GenerateReporter" /> 或者testng.xml加入
@Listeners({report.GenerateReporter.class})

目录结构:

代码:

package report;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.testng.ITestNGMethod;

public class DataBean {
    private int excludeTestsSize; //未执行的test数量
    private int passedTestsSize; //测试通过的数量
    private int failedTestsSize; //测试失败的数量
    private int skippedTestsSize; //测试跳过的数量
    private int allTestsSize; //全部执行的测试的数量
    private ITestNGMethod[] allTestsMethod; //全部执行的测试方法
    private Collection<ITestNGMethod> excludeTestsMethod; //未执行的测试方法
    private String testsTime; //测试耗时
    private String passPercent; //测试通过率
    private String testName; //测试方法名
    private String className; //测试类名
    private String duration; //单个测试周期
    private String params; //测试用参数
    private String description; //测试描述
    private List<String> output; //Reporter Output
    private String dependMethod; //测试依赖方法
    private Throwable throwable; //测试异常原因
    private StackTraceElement[] stackTrace; // 异常堆栈信息

    public int getExcludeTestsSize() {
        return excludeTestsSize;
    }

    public void setExcludeTestsSize(int excludeTestsSize) {
        this.excludeTestsSize = excludeTestsSize;
    }

    public int getPassedTestsSize() {
        return passedTestsSize;
    }

    public void setPassedTestsSize(int passedTestsSize) {
        this.passedTestsSize = passedTestsSize;
    }

    public int getFailedTestsSize() {
        return failedTestsSize;
    }

    public void setFailedTestsSize(int failedTestsSize) {
        this.failedTestsSize = failedTestsSize;
    }

    public int getSkippedTestsSize() {
        return skippedTestsSize;
    }

    public void setSkippedTestsSize(int skippedTestsSize) {
        this.skippedTestsSize = skippedTestsSize;
    }

    public int getAllTestsSize() {
        return allTestsSize;
    }

    public void setAllTestsSize(int allTestsSize) {
        this.allTestsSize = allTestsSize;
    }

    public String getPassPercent() {
        return passPercent;
    }

    public void setPassPercent(String passPercent) {
        this.passPercent = passPercent;
    }

    public String getTestName() {
        return testName;
    }

    public void setTestName(String testName) {
        this.testName = testName;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getDuration() {
        return duration;
    }

    public void setDuration(String duration) {
        this.duration = duration;
    }

    public String getParams() {
        return params;
    }

    public void setParams(String params) {
        this.params = params;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public List<String> getOutput() {
        return output;
    }

    public void setOutput(List<String> output) {
        this.output = output;
    }

    public String getDependMethod() {
        return dependMethod;
    }

    public void setDependMethod(String dependMethod) {
        this.dependMethod = dependMethod;
    }

    public Throwable getThrowable() {
        return throwable;
    }

    public void setThrowable(Throwable throwable2) {
        this.throwable = throwable2;
    }

    public StackTraceElement[] getStackTrace() {
        return stackTrace;
    }

    public void setStackTrace(StackTraceElement[] stackTrace) {
        this.stackTrace = stackTrace;
    }

    public void setTestsTime(String testsTime) {
        this.testsTime = testsTime;
    }

    public String getTestsTime() {
        return testsTime;
    }

    public void setAllTestsMethod(ITestNGMethod[] allTestsMethod) {
        this.allTestsMethod = allTestsMethod;
    }

    public ITestNGMethod[] getAllTestsMethod() {
        return allTestsMethod;
    }

    public void setExcludeTestsMethod(Collection<ITestNGMethod> excludeTestsMethod) {
        this.excludeTestsMethod = excludeTestsMethod;
    }

    public Collection<ITestNGMethod> getExcludeTestsMethod() {
        return excludeTestsMethod;
    }

    @Override
    public String toString() {
        return "DataBean{" +
                "excludeTestsSize=" + excludeTestsSize +
                ", passedTestsSize=" + passedTestsSize +
                ", failedTestsSize=" + failedTestsSize +
                ", skippedTestsSize=" + skippedTestsSize +
                ", allTestsSize=" + allTestsSize +
                ", allTestsMethod=" + Arrays.toString(allTestsMethod) +
                ", excludeTestsMethod=" + excludeTestsMethod +
                ", testsTime='" + testsTime + '\'' +
                ", passPercent='" + passPercent + '\'' +
                ", testName='" + testName + '\'' +
                ", className='" + className + '\'' +
                ", duration='" + duration + '\'' +
                ", params='" + params + '\'' +
                ", description='" + description + '\'' +
                ", output=" + output +
                ", dependMethod='" + dependMethod + '\'' +
                ", throwable=" + throwable +
                ", stackTrace=" + Arrays.toString(stackTrace) +
                '}';
    }
}

 

package report;

import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.testng.IReporter;
import org.testng.IResultMap;
import org.testng.ISuite;
import org.testng.ISuiteResult;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.xml.XmlSuite;
import freemarker.template.*;

public class GenerateReporter implements IReporter {
    @Override
    public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites,
                               String outputDirectory) {
        // TODO Auto-generated method stub
        try {
            //freemaker的配置
            Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
            cfg.setClassForTemplateLoading(this.getClass(),"/templates");
            cfg.setDefaultEncoding("UTF-8");
            cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
            //freemaker的模板文件
            Template temp = cfg.getTemplate("overview.ftl");

            Map context = new HashMap();


            for (ISuite suite : suites) {
                Map<String, ISuiteResult> suiteResults = suite.getResults();
                for (ISuiteResult suiteResult : suiteResults.values()) {
                    ReporterData data = new ReporterData();
                    ITestContext testContext = suiteResult.getTestContext();
                    // 把数据填入上下文
                    context.put("overView", data.testContext(testContext));//测试结果汇总信息
                    //ITestNGMethod[] allTests = testContext.getAllTestMethods();//所有的测试方法
                    //Collection<ITestNGMethod> excludeTests = testContext.getExcludedMethods();//未执行的测试方法
                    IResultMap passedTests = testContext.getPassedTests();//测试通过的测试方法
                    IResultMap failedTests = testContext.getFailedTests();//测试失败的测试方法
                    IResultMap skippedTests = testContext.getSkippedTests();//测试跳过的测试方法

                    context.put("pass", data.testResults(passedTests, ITestResult.SUCCESS));
                    context.put("fail", data.testResults(failedTests, ITestResult.FAILURE));
                    context.put("skip", data.testResults(skippedTests, ITestResult.FAILURE));

                }
            }
            System.out.println(context.get("overView").toString());
            // 输出流
            //Writer writer = new BufferedWriter(new FileWriter("report.html"));
            OutputStream out=new FileOutputStream("target/report.html");
            Writer writer = new BufferedWriter(new OutputStreamWriter(out,"utf-8"));//解决乱码问题
            // 转换输出
            temp.process(context,writer);
            writer.flush();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


}

 

package report;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;


import org.testng.IResultMap;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.Reporter;




public class ReporterData {
    // 测试结果Set<ITestResult>转为list,再按执行时间排序 ,返回list
    public List<ITestResult> sortByTime(Set<ITestResult> str) {
        List<ITestResult> list = new ArrayList<ITestResult>();
        for (ITestResult r : str) {
            list.add(r);
        }
        Collections.sort(list);
        return list;


    }

    public DataBean testContext(ITestContext context) {
        // 测试结果汇总数据
        DataBean data = new DataBean();
        ReportUnits units = new ReportUnits();
        IResultMap passedTests = context.getPassedTests();
        IResultMap failedTests= context.getFailedTests();
        IResultMap skipedTests = context.getSkippedTests();
        //全部测试周期方法,包括beforetest,beforeclass,beforemethod,aftertest,afterclass,aftermethod
        //IResultMap passedConfigurations =context.getPassedConfigurations();
        //IResultMap failedConfigurations =context.getFailedConfigurations();
        //IResultMap skipedConfigurations =context.getSkippedConfigurations();
        Collection<ITestNGMethod> excludeTests = context.getExcludedMethods();

        int passedTestsSize = passedTests.size();
        int failedTestsSize = failedTests.size();
        int skipedTestsSize = skipedTests.size();
        int excludeTestsSize = excludeTests.size();
        //所有测试结果的数量=测试pass+fail+skip的和,因为数据驱动一个测试方法有多次执行的可能,导致方法总数并不等于测试总数
        int allTestsSize= passedTestsSize+failedTestsSize+skipedTestsSize;
        data.setAllTestsSize(allTestsSize);
        data.setPassedTestsSize(passedTestsSize);
        data.setFailedTestsSize(failedTestsSize);
        data.setSkippedTestsSize(skipedTestsSize);
        data.setExcludeTestsSize(excludeTestsSize);
        data.setTestsTime(units.getTestDuration(context));
        data.setPassPercent(units.formatPercentage(passedTestsSize, allTestsSize));
        data.setAllTestsMethod(context.getAllTestMethods());
        data.setExcludeTestsMethod(context.getExcludedMethods());

        return data;


    }


    public List<DataBean> testResults(IResultMap map, int status) {
        // 测试结果详细数据
        List<DataBean> list = new ArrayList<DataBean>();
        ReportUnits units = new ReportUnits();
        map.getAllResults().size();
        for (ITestResult result : sortByTime(map.getAllResults())) {
            DataBean data = new DataBean();
            data.setTestName(result.getName());
            data.setClassName(result.getTestClass().getName());
            data.setDuration(units.formatDuration(result.getEndMillis()
                    - result.getStartMillis()));
            data.setParams(units.getParams(result));
            data.setDescription(result.getMethod().getDescription());
            data.setOutput(Reporter.getOutput(result));
            data.setDependMethod(units.getDependMethods(result));
            data.setThrowable(result.getThrowable());
            if (result.getThrowable() != null) {
                data.setStackTrace(result.getThrowable().getStackTrace());
            }
            list.add(data);
        }
        return list;
    }

}

 

package report;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.Reporter;

public class ReportUnits {
    private static final NumberFormat DURATION_FORMAT = new DecimalFormat("#0.000");
    private static final NumberFormat PERCENTAGE_FORMAT = new DecimalFormat("#0.00%");
    /**
     *测试消耗时长
     *return 秒,保留3位小数
     */
    public String getTestDuration(ITestContext context){
        long duration;
        duration=context.getEndDate().getTime()-context.getStartDate().getTime();
        return formatDuration(duration);
    }

    public String formatDuration(long elapsed)
    {
        double seconds = (double) elapsed / 1000;
        return DURATION_FORMAT.format(seconds);
    }
    /**
     *测试通过率
     *return 2.22%,保留2位小数
     */
    public String formatPercentage(int numerator, int denominator)
    {
        return PERCENTAGE_FORMAT.format(numerator / (double) denominator);
    }

    /**
     * 获取方法参数,以逗号分隔
     * @param result
     * @return
     */
    public String getParams(ITestResult result){
        Object[] params = result.getParameters();
        List<String> list = new ArrayList<String>(params.length);
        for (Object o:params){
            list.add(renderArgument(o));
        }
        return  commaSeparate(list);
    }
    /**
     * 获取依赖的方法
     * @param result
     * @return
     */
    public String getDependMethods(ITestResult result){
        String[] methods=result.getMethod().getMethodsDependedUpon();
        return commaSeparate(Arrays.asList(methods));
    }
    /**
     * 堆栈轨迹,暂不确定怎么做,放着先
     * @param throwable
     * @return
     */
    public String getCause(Throwable throwable){
        StackTraceElement[] stackTrace=throwable.getStackTrace(); //堆栈轨迹
        List<String> list = new ArrayList<String>(stackTrace.length);
        for (Object o:stackTrace){
            list.add(renderArgument(o));
        }
        return  commaSeparate(list);
    }
    /**
     * 获取全部日志输出信息
     * @return
     */
    public List<String> getAllOutput(){
        return Reporter.getOutput();
    }

    /**
     * 按testresult获取日志输出信息
     * @param result
     * @return
     */
    public List<String> getTestOutput(ITestResult result){
        return Reporter.getOutput(result);
    }


    /*将object 转换为String*/
    private String renderArgument(Object argument)
    {
        if (argument == null)
        {
            return "null";
        }
        else if (argument instanceof String)
        {
            return "\"" + argument + "\"";
        }
        else if (argument instanceof Character)
        {
            return "\'" + argument + "\'";
        }
        else
        {
            return argument.toString();
        }
    }
    /*将集合转换为以逗号分隔的字符串*/
    private String commaSeparate(Collection<String> strings)
    {
        StringBuilder buffer = new StringBuilder();
        Iterator<String> iterator = strings.iterator();
        while (iterator.hasNext())
        {
            String string = iterator.next();
            buffer.append(string);
            if (iterator.hasNext())
            {
                buffer.append(", ");
            }
        }
        return buffer.toString();
    }
}

 

package report;


import org.testng.ITestResult;

public class TestResultSort implements Comparable<ITestResult> {
    private Long order;
    @Override
    public int compareTo(ITestResult arg0) {
        // TODO Auto-generated method stub
        return this.order.compareTo( arg0.getStartMillis());//按test开始时间排序
    }

}

 

完美好看的html报告freemaker模板

<?xml version="1.0" encoding="utf-8" ?>

<head>
<title>test</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="description" content="TestNG unit test results." />
<style type="text/css">
body
{
margin: 10px 20px;
font-size: 14px;
font-family: "Arial","Microsoft YaHei","黑体","宋体",sans-serif;
}
/*table*/
/*{*/
/*border-collapse: collapse;*/
/*text-align: center;*/
/*font-size: 14px;*/
/*}*/
/*table td, table th*/
/*{*/
/*border: 2px solid #cc6f4a;*/
/*color: #666;*/
/*height: 20px;*/
/*text-align: center;*/
/*padding: 3px 3px;*/
/*}*/
/*table thead th*/
/*{*/
/**/
/*width: 100px;*/
/*}*/
/*table tr:nth-child(odd)*/
/*{*/
/*background: #fff;*/
/*}*/
/*table tr:nth-child(even)*/
/*{*/
/*background: #c9dafa;*/
/*}*/
.successBtn {
width: 60px;
padding:3px;
background-color: #58ab48;
border-color: #58ab48;
color: #fff;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px; /* future proofing */
-khtml-border-radius: 10px; /* for old Konqueror browsers */
text-align: center;
vertical-align: middle;
border: 1px solid transparent;
font-weight: 500;
/*font-size:125%*/
}
.failBtn{
width: 60px;
padding:3px;
background-color: #ab2e2d;
border-color: #ab2e2d;
color: #fff;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px; /* future proofing */
-khtml-border-radius: 10px; /* for old Konqueror browsers */
text-align: center;
vertical-align: middle;
border: 1px solid transparent;
font-weight: 500;
/*font-size:125%*/
}
</style>

<style>
/* Border styles */
.tabNoBorder thead, .tabNoBorder tr {
border-top-width: 1px;
border-top-style: solid;
border-top-color: rgb(211, 202, 221);
}
.tabNoBorder {
border-bottom-width: 1px;
border-bottom-style: solid;
border-bottom-color: rgb(211, 202, 221);
}

/* Padding and font style */
.tabNoBorder td, .tabNoBorder th {
padding: 5px 10px;
font-size: 14px;
font-family: Verdana;
color: rgb(95, 74, 121);
}

/* Alternating background colors */
.tabNoBorder tr:nth-child(even) {
background: rgb(223, 216, 232)
}
.tabNoBorder tr:nth-child(odd) {
background: #FFF
}
</style>


</head>
<body>
<br/>
<h2>Summary</h2>
<table id="summary" class="tabNoBorder">
<tr class="columnHeadings">
<th>用例总数</th>
<th>未执行用例数</th>
<th>执行通过</th>
<th>执行失败</th>
<th>跳过用例数</th>
<th>执行时间(s)</th>
<th>用例通过率</th>
<#--<th>alltestMethod</th>-->
<#--<th>excluedMethod</th>-->
</tr>

<tr>
<td>${overView.allTestsSize}</td>
<td>${overView.excludeTestsSize}</td>
<td>${overView.passedTestsSize}</td>
<td>${overView.failedTestsSize}</td>
<td>${overView.skippedTestsSize}</td>
<td>${overView.testsTime}</td>
<td>${overView.passPercent}</td>
<#--<td>-->
<#--<#list overView.allTestsMethod as item>-->
<#--${item.methodName}-->
<#--</#list>-->
<#--</td>-->
<#--<td>-->
<#--<#list overView.excludeTestsMethod as item1>-->
<#--${item1.methodName}-->
<#--</#list>-->
<#--</td>-->
</tr>
</table>
<br/><br/>
<h2>Detail</h2>
<table class="tabNoBorder">
<tr class="columnHeadings">
<th>编号</th>
<th>Class</th>
<th>MethodName</th>
<th>用例描述</th>
<th>执行结果</th>
<th>执行时间(s)</th>
<th>报错信息</th>
</tr>
<#assign caseNo = 0>
<#list fail as failCase>
<tr>
<#assign caseNo=caseNo+1>
<td>${caseNo}</td>
<td>${failCase.className}</td>
<td>${failCase.testName}</td>
<td>${failCase.description!}</td>
<td><div class="failBtn">Fail</div></td>
<td>${failCase.duration!}</td>
<td>${failCase.throwable!}</td>
</tr>
</#list>
<#list pass as passCase>
<tr>
<#assign caseNo=caseNo+1>
<td>${caseNo}</td>
<td>${passCase.className}</td>
<td>${passCase.testName}</td>
<td>${passCase.description!}</td>
<td><div class="successBtn">Success</div></td>
<td>${passCase.duration!}</td>
<td>${passCase.throwable!}</td>
</tr>
</#list>


</table>



<br/><br/>



</body>
</html>

 

转载于:https://www.cnblogs.com/zipon/p/7210423.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值