问题描述
TestNG 生成可通过电子邮件发送的报告.我已经看到可以使用 Listeners 自定义此报告.但无法得到我想要的.我的要求是在本报告的摘要部分包含额外的详细信息.我希望能够添加可能是一个新表或额外的列来显示测试执行的环境详细信息.
试图附上截图,但显然遗漏了一些东西,但没有出现.
这就是我的框架.我会试着解释一下(对不起我的英语)
复制 ReporterListenerAdapter.java 并重命名为 MyReporterListenerAdapter.java,放在你的 java 项目中(例如/listener 文件夹)
public class MyReporterListenerAdapter 实现 IReporter {public void generateReport(List xml, Listsuites, String outdir) {}}
接下来,复制 ReporterListener.java 并重命名为 MyReporterListener.java
此处粘贴太多代码,但在 createWriter 函数上更改报告名称.例如:emailable-MyFramework-report".
MyReporterListener.java
public class MyReporterListener extends MyReporterListenerAdapter {private static final Logger L = Logger.getLogger(MyReporterListener.class);//~ 实例字段 --------------------------------------------------私人 PrintWriter m_out;私人 int m_row;私有整数 m_testIndex;私有 int m_methodIndex;私人扫描仪扫描仪;//~ 方法 --------------------------------------------------------------/** 创建运行摘要 */@覆盖public void generateReport(List xml, Listsuites,字符串外目录) {尝试 {m_out = createWriter(outdir);} catch (IOException e) {L.error("输出文件", e);返回;}startHtml(m_out);generateSuiteSummaryReport(suites);generateMethodSummaryReport(suites);generateMethodDetailReport(suites);endHtml(m_out);m_out.flush();m_out.close();}受保护的 PrintWriter createWriter(String outdir) 抛出 IOException {java.util.Date now = new Date();新文件(outdir).mkdirs();返回新的 PrintWriter(new BufferedWriter(new FileWriter(new File(outdir, "emailable-FON-report"+ DateFunctions.dateToDayAndTimeForFileName(现在)+ ".html"))));}/*** 创建一个表格,显示每个测试方法的亮点,并带有指向* 方法细节*/protected void generateMethodSummaryReport(Listsuites) {m_methodIndex = 0;startResultSummaryTable("methodOverview");int testIndex = 1;对于(ISuite 套件:套件){如果 (suites.size() > 1) {titleRow(suite.getName(), 5);}映射r = 套件.getResults();for (ISuiteResult r2 : r.values()) {ITestContext testContext = r2.getTestContext();String testName = testContext.getName();m_testIndex = 测试索引;结果总结(套件,testContext.getFailedConfigurations(),testName, "failed", "(配置方法)");resultSummary(suite, testContext.getFailedTests(), testName,失败的", "");resultSummary(suite, testContext.getSkippedConfigurations(),测试名称,跳过",(配置方法)");resultSummary(suite, testContext.getSkippedTests(), testName,"跳过", "");resultSummary(suite, testContext.getPassedTests(), testName,"通过", "");测试索引++;}}m_out.println("</table>");}/** 创建一个部分,显示每种方法的已知结果 */protected void generateMethodDetailReport(Listsuite) {m_methodIndex = 0;对于(ISuite 套件:套件){映射r = 套件.getResults();for (ISuiteResult r2 : r.values()) {ITestContext testContext = r2.getTestContext();如果 (r.values().size() > 0) {m_out.println("" + testContext.getName() + "
");}resultDetail(testContext.getFailedConfigurations());resultDetail(testContext.getFailedTests());resultDetail(testContext.getSkippedConfigurations());resultDetail(testContext.getSkippedTests());resultDetail(testContext.getPassedTests());}}}/*** @param 测试*/private void resultSummary(ISuite 套件,IResultMap 测试,字符串测试名称,字符串样式,字符串详细信息) {如果 (tests.getAllResults().size() > 0) {StringBuffer buff = new StringBuffer();String lastClassName = "";int mq = 0;int cq = 0;for (ITestNGMethod 方法: getMethodSet(tests,suite)) {m_row += 1;m_methodIndex += 1;ITestClass testClass = method.getTestClass();String className = testClass.getName();如果(MQ == 0){String id = (m_testIndex == null ? null : "t"+ Integer.toString(m_testIndex));titleRow(testname + " — " + style + details, 5, id);m_testIndex = null;}if (!className.equalsIgnoreCase(lastClassName)) {如果 (mq > 0) {cq += 1;m_out.print("<tr class=\"" + style+ (cq % 2 == 0 ? "偶数" : "奇数") + "\">"+ " 1) {m_out.print(" rowspan=\"" + mq + "\"");}m_out.println(">" + lastClassName + "" + buff);}MQ = 0;buff.setLength(0);lastClassName = 类名;}设置resultSet = tests.getResults(method);长尾 = Long.MIN_VALUE;长开始 = Long.MAX_VALUE;for (ITestResult testResult : tests.getResults(method)) {如果(testResult.getEndMillis()>结束){end = testResult.getEndMillis();}如果(testResult.getStartMillis() 1) {buff.append("<tr class=\"" + style+ (cq % 2 == 0 ? "odd" : "even") + "\">");}字符串描述 = method.getDescription();字符串 testInstanceName = 结果集.toArray(new ITestResult[] {})[0].getTestName();buff.append("<td><a href=\"#m"+ m_methodIndex+ "\">"+ 限定名称(方法)+ " "+ (description != null && description.length() > 0 ? "(\""+ 描述 + "\")":"")+ "</a>"+ (null == testInstanceName ? "" : "
("+ testInstanceName + ")") + "</td>"+ ""+ resultSet.size() + "</td>"+ "<td>"+ 开始 + "</td>"+ " "+ (结束 - 开始) + "</td>"+ "</tr>");}如果 (mq > 0) {cq += 1;m_out.print("<tr class=\"" + style+ (cq % 2 == 0 ? "even" : "odd") + "\">" + " 1) {m_out.print(" rowspan=\"" + mq + "\"");}m_out.println(">" + lastClassName + " " + buff);}}}/** 开始并定义列结果汇总表 */私有无效 startResultSummaryTable(字符串样式){tableStart(样式,摘要");m_out.println("<tr><th>Class</th>"+ "<方法</th><br/>场景#</th><开始</th><th>时间<br/>(ms)<;/th>");m_row = 0;}私有字符串限定名称(ITestNGMethod 方法){StringBuilder 插件 = new StringBuilder();String[] 组 = method.getGroups();int 长度 = groups.length;如果(长度 > 0 && !"basic".equalsIgnoreCase(groups[0])) {addon.append("(");for (int i = 0; i < length; i++) {如果 (i > 0) {addon.append(", ");}addon.append(groups[i]);}addon.append(")");}返回<b>"+ method.getMethodName() + "</b> " + 插件;}私有无效结果详细信息(IResultMap测试){for (ITestResult 结果:tests.getAllResults()) {ITestNGMethod 方法 = result.getMethod();m_methodIndex++;String cname = method.getTestClass().getName();m_out.println("<h2 id=\"m" + m_methodIndex + "\">" + cname + ":"+ method.getMethodName() + "</h2>");设置resultSet = tests.getResults(method);generateForResult(result, method, resultSet.size());m_out.println("<p class=\"totop\"><a href=\"#summary\">返回摘要</a></p>");}}/*** 写入堆栈跟踪的第一行** @param 测试*/私人无效getShortException(IResultMap测试){for (ITestResult 结果:tests.getAllResults()) {m_methodIndex++;Throwable 异常 = result.getThrowable();列表msgs = Reporter.getOutput(result);boolean hasReporterOutput = msgs.size() >0;boolean hasThrowable = 异常 != null;如果(hasThrowable){boolean WantsMinimalOutput = result.getStatus() == ITestResult.SUCCESS;如果(hasReporterOutput){m_out.print(""+(想要MinimalOutput?预期异常": "失败") + "
");}//获取堆栈跟踪的第一行String str = Utils.stackTrace(exception, true)[0];扫描仪=新扫描仪(str);字符串 firstLine = 扫描仪.nextLine();m_out.println(firstLine);}}}/*** 写入所有参数** @param 测试*/私有无效getParameters(IResultMap测试){for (ITestResult 结果:tests.getAllResults()) {m_methodIndex++;Object[] 参数 = result.getParameters();boolean hasParameters = 参数 != null &&参数.length >0;如果(有参数){对于(对象 p:参数){m_out.println(Utils.escapeHtml(Utils.toString(p)) + " | ");}}}}private void generateForResult(ITestResult ans, ITestNGMethod 方法,int resultSetSize) {Object[] 参数 = ans.getParameters();boolean hasParameters = 参数 != null &&参数.length >0;如果(有参数){tableStart("结果", null);m_out.print("<tr class=\"param\">");for (int x = 1; x Param." + x + "");}m_out.println("</tr>");m_out.print("<tr class=\"param stripe\">");对于(对象 p:参数){m_out.println("" + Utils.escapeHtml(Utils.toString(p))+ "</td>");}m_out.println("</tr>");}列表msgs = Reporter.getOutput(ans);boolean hasReporterOutput = msgs.size() >0;Throwable 异常 = ans.getThrowable();boolean hasThrowable = 异常 != null;如果(hasReporterOutput || hasThrowable){如果(有参数){m_out.print("<tr><td");if (parameters.length > 1) {m_out.print(" colspan=\"" + parameters.length + "\"");}m_out.println(">");} 别的 {m_out.println("");}如果(hasReporterOutput){如果(hasThrowable){m_out.println("<h3>测试消息</h3>");}for (String line : msgs) {m_out.println(line + "
");}}如果(hasThrowable){boolean WantsMinimalOutput = ans.getStatus() == ITestResult.SUCCESS;如果(hasReporterOutput){m_out.println(""+(想要MinimalOutput?预期异常": "失败") + "
");}generateExceptionReport(异常,方法);}如果(有参数){m_out.println("</td></tr>");} 别的 {m_out.println("</div>");}}如果(有参数){m_out.println("</table>");}}protected void generateExceptionReport(Throwable 异常,ITestNGMethod 方法) {m_out.print("<div class=\"stacktrace\">");m_out.print(Utils.stackTrace(exception, true)[0]);m_out.println("</div>");}/*** 由于方法将按时间顺序排序,我们希望返回* ITestNGMethod 来自调用的方法.*/私有集合getMethodSet(IResultMap 测试,ISuite 套件) {列表r = Lists.newArrayList();列表被调用的方法 = suite.getAllInvokedMethods();for (IInvokedMethod im:invokeMethods) {如果 (tests.getAllMethods().contains(im.getTestMethod())) {r.add(im);}}Arrays.sort(r.toArray(new IInvokedMethod[r.size()]), new TestSorter());列表结果 = Lists.newArrayList();//添加所有调用的方法for (IInvokedMethod m : r) {result.add(m.getTestMethod());}//添加我们未调用(例如跳过)的所有方法//还没有添加for (ITestNGMethod m : tests.getAllMethods()) {如果 (!result.contains(m)) {结果.add(m);}}返回结果;}@SuppressWarnings("未使用")public void generateSuiteSummaryReport(Listsuites) {tableStart("testOverview", null);m_out.print("");tableColumnStart("测试");tableColumnStart("方法
通过");tableColumnStart("场景<br/>通过");tableColumnStart("#跳过");tableColumnStart("# 失败");tableColumnStart("错误信息");tableColumnStart("参数");tableColumnStart("开始<br/>时间");tableColumnStart("结束<br/>时间");tableColumnStart("Total<br/>Time");tableColumnStart("包含的<br/>组");tableColumnStart("排除的<br/>组");m_out.println("</tr>");NumberFormat formatter = new DecimalFormat("#,##0.0");int qty_tests = 0;int qty_pass_m = 0;int qty_pass_s = 0;int qty_skip = 0;int qty_fail = 0;long time_start = Long.MAX_VALUE;long time_end = Long.MIN_VALUE;m_testIndex = 1;对于(ISuite 套件:套件){如果 (suites.size() > 1) {titleRow(suite.getName(), 8);}映射测试 = suite.getResults();for (ISuiteResult r : tests.values()) {qty_tests += 1;ITestContext 概览 = r.getTestContext();startSummaryRow(overview.getName());int q = getMethodSet(overview.getPassedTests(),suite).size();qty_pass_m += q;summaryCell(q, Integer.MAX_VALUE);q = 概览.getPassedTests().size();qty_pass_s += q;summaryCell(q, Integer.MAX_VALUE);q = getMethodSet(overview.getSkippedTests(),suite).size();qty_skip += q;summaryCell(q, 0);q = getMethodSet(overview.getFailedTests(),suite).size();qty_fail += q;summaryCell(q, 0);//新的//发现插入错误m_out.print("<td class=\"numi" + (true ? "" : "_attn") + "\">");getShortException(overview.getFailedTests());getShortException(overview.getSkippedTests());m_out.println("</td>");//新的//为每个测试用例添加参数(失败或通过)m_out.print("<td class=\"numi" + (true ? "" : "_attn") + "\">");//编写操作系统和浏览器//m_out.println(suite.getParameter("os").substring(0, 3) +//" | "//+ suite.getParameter("browser").substring(0, 3) + " | ");getParameters(overview.getFailedTests());getParameters(overview.getPassedTests());getParameters(overview.getSkippedTests());m_out.println("</td>");//新的摘要单元格(DateFunctions.dateToDayAndTime(overview.getStartDate()),真的);m_out.println("</td>");摘要单元格(DateFunctions.dateToDayAndTime(overview.getEndDate()),真的);m_out.println("</td>");time_start = Math.min(overview.getStartDate().getTime(),时间开始);time_end = Math.max(overview.getEndDate().getTime(), time_end);摘要单元格(formatter.format((overview.getEndDate().getTime() - 概述.getStartDate().getTime())/1000.)+ "秒", 真);summaryCell(overview.getIncludedGroups());summaryCell(overview.getExcludedGroups());m_out.println("</tr>");m_testIndex++;}}如果 (qty_tests > 1) {m_out.println("<tr class=\"total\"><td>Total</td>");summaryCell(qty_pass_m, Integer.MAX_VALUE);summaryCell(qty_pass_s, Integer.MAX_VALUE);summaryCell(qty_skip, 0);summaryCell(qty_fail, 0);summaryCell(" ", true);summaryCell(" ", true);summaryCell(" ", true);summaryCell(" ", true);摘要单元格(formatter.format(((time_end - time_start)/1000.)/60.)+ "分钟", 真);m_out.println("<td colspan=\"3\"> </td></tr>");}m_out.println("</table>");}私人无效summaryCell(字符串[] val){StringBuffer b = new StringBuffer();for (String v: val) {b.append(v + " ");}summaryCell(b.toString(), true);}private void summaryCell(String v, boolean isgood) {m_out.print("<td class=\"numi" + (isgood ?"" : "_attn") + "\">" + v+ "</td>");}私有无效 startSummaryRow(字符串标签){m_row += 1;m_out.print("<tr"+ (m_row % 2 == 0 ?" class=\"stripe\"" : "")+ "><td style=\"text-align:left;padding-right:2em\"><a href=\"#t"+ m_testIndex + "\">" + label + "" + "");}private void summaryCell(int v, int maxexpected) {summaryCell(String.valueOf(v), v ");m_row = 0;}私有无效表列开始(字符串标签){m_out.print("" + label + " ");}私有无效titleRow(字符串标签,int cq){titleRow(label, cq, null);}私有无效titleRow(字符串标签,int cq,字符串id){m_out.print("<tr");如果(ID!= null){m_out.print(" id=\"" + id + "\"");}m_out.println("><th colspan=\"" + cq + "\">" + label + "</th></tr>");m_row = 0;}/** 启动 HTML 流 */protected void startHtml(PrintWriter out) {out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">");out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\">");out.println("");out.println("<title>赫克托·弗洛雷斯-TestNG 报告</title>");out.println("<style type=\"text/css\">");out.println("table {margin-bottom:10px;border-collapse:collapse;empty-cells:show}");out.println("td,th {border:1px solid #009;padding:.25em .5em}");out.println(".result th {vertical-align:bottom}");out.println(".param th {padding-left:1em;padding-right:1em}");out.println(".param td {padding-left:.5em;padding-right:2em}");out.println(".stripe td,.stripe th {background-color: #E6EBF9}");out.println(".numi,.numi_attn {text-align:right}");out.println(".total td {font-weight:bold}");out.println(".passedodd td {background-color: #0A0}");out.println(".passedeven td {background-color: #3F3}");out.println(".skippodd td {background-color: #CCC}");out.println(".skippodd td {background-color: #DDD}");out.println(".failedodd td,.numi_attn {background-color: #F33}");out.println(".failedeven td,.stripe .numi_attn {background-color: #D00}");out.println(".stacktrace {white-space:pre;font-family:monospace}");out.println(".totop {font-size:85%;text-align:center;border-bottom:2px solid #000}");out.println("</style>");out.println("</head>");out.println("");}/** 结束 HTML 流 */protected void endHtml(PrintWriter out) {out.println("<center> Hector Flores 定制的报告 [[email protected]] </center>");out.println("</body></html>");}//~ 内部类 --------------------------------------------------------/** 按类名和方法名排列方法 */私有类 TestSorter 实现了 Comparator{//~ 方法//-------------------------------------------------------------/** 按类名和方法名排列方法 */@覆盖公共 int 比较(IInvokedMethod o1,IInvokedMethod o2){//System.out.println("比较" + o1.getMethodName() + " " +//o1.getDate()//+ " and " + o2.getMethodName() + " " + o2.getDate());返回 (int) (o1.getDate() - o2.getDate());//int r = ((T) o1).getTestClass().getName().compareTo(((T)//o2).getTestClass().getName());//如果 (r == 0) {//r = ((T) o1).getMethodName().compareTo(((T) o2).getMethodName());//}//返回 r;}}}通过这些步骤,您的听众已经准备好聆听了.
怎么称呼?
如果您使用 testng.xml,请添加以下几行:
<listener class-name='[your_class_path].MyReporterListener'/></听众>
如果您从 Java 类运行测试,请添加以下几行:
private final static MyReporterListener frl = new MyReporterListener();TestNG testng = new TestNG();testng.addListener(frl);
通过这些步骤,当您执行测试时,您将获得两份可通过电子邮件发送的报告,定制的和原始的.
现在是时候对您的报告进行皮条客了.
就我而言,我必须添加错误消息、参数和时间(开始和结束),因为如果您想粘贴到 excel 文件上,这非常有用.
我的定制报告:
主要需要修改 generateSuiteSummaryReport(List suites) 函数.
玩那个,问我你是否有任何问题.
TestNG generates an emailable report. I have seen that this report can be customized by using Listeners. But could not get what i wanted. My requirement is to include extra details in the summary section of this report. I want to be able to add may be a new table or extra columns to show the environment details of the test execution.
Trying to attach a screenshot but apparently missing something and it does not come up.
解决方案 That is what I have on my framework. I'll try to explain it (sorry my English)
Copy ReporterListenerAdapter.java and rename as MyReporterListenerAdapter.java, put it on your java project (/listener folder for example)
public class MyReporterListenerAdapter implements IReporter {
public void generateReport(List<XmlSuite> xml, List<ISuite> suites, String outdir) {}
}
Next, copy ReporterListener.java and rename as MyReporterListener.java
Too much code to paste here, but on createWriter function change the report name. For example: "emailable-MyFramework-report".
MyReporterListener.java
public class MyReporterListener extends MyReporterListenerAdapter {
private static final Logger L = Logger.getLogger(MyReporterListener.class);
// ~ Instance fields ------------------------------------------------------
private PrintWriter m_out;
private int m_row;
private Integer m_testIndex;
private int m_methodIndex;
private Scanner scanner;
// ~ Methods --------------------------------------------------------------
/** Creates summary of the run */
@Override
public void generateReport(List<XmlSuite> xml, List<ISuite> suites,
String outdir) {
try {
m_out = createWriter(outdir);
} catch (IOException e) {
L.error("output file", e);
return;
}
startHtml(m_out);
generateSuiteSummaryReport(suites);
generateMethodSummaryReport(suites);
generateMethodDetailReport(suites);
endHtml(m_out);
m_out.flush();
m_out.close();
}
protected PrintWriter createWriter(String outdir) throws IOException {
java.util.Date now = new Date();
new File(outdir).mkdirs();
return new PrintWriter(new BufferedWriter(new FileWriter(new File(
outdir, "emailable-FON-report"
+ DateFunctions.dateToDayAndTimeForFileName(now)
+ ".html"))));
}
/**
* Creates a table showing the highlights of each test method with links to
* the method details
*/
protected void generateMethodSummaryReport(List<ISuite> suites) {
m_methodIndex = 0;
startResultSummaryTable("methodOverview");
int testIndex = 1;
for (ISuite suite : suites) {
if (suites.size() > 1) {
titleRow(suite.getName(), 5);
}
Map<String, ISuiteResult> r = suite.getResults();
for (ISuiteResult r2 : r.values()) {
ITestContext testContext = r2.getTestContext();
String testName = testContext.getName();
m_testIndex = testIndex;
resultSummary(suite, testContext.getFailedConfigurations(),
testName, "failed", " (configuration methods)");
resultSummary(suite, testContext.getFailedTests(), testName,
"failed", "");
resultSummary(suite, testContext.getSkippedConfigurations(),
testName, "skipped", " (configuration methods)");
resultSummary(suite, testContext.getSkippedTests(), testName,
"skipped", "");
resultSummary(suite, testContext.getPassedTests(), testName,
"passed", "");
testIndex++;
}
}
m_out.println("</table>");
}
/** Creates a section showing known results for each method */
protected void generateMethodDetailReport(List<ISuite> suites) {
m_methodIndex = 0;
for (ISuite suite : suites) {
Map<String, ISuiteResult> r = suite.getResults();
for (ISuiteResult r2 : r.values()) {
ITestContext testContext = r2.getTestContext();
if (r.values().size() > 0) {
m_out.println("<h1>" + testContext.getName() + "</h1>");
}
resultDetail(testContext.getFailedConfigurations());
resultDetail(testContext.getFailedTests());
resultDetail(testContext.getSkippedConfigurations());
resultDetail(testContext.getSkippedTests());
resultDetail(testContext.getPassedTests());
}
}
}
/**
* @param tests
*/
private void resultSummary(ISuite suite, IResultMap tests, String testname,
String style, String details) {
if (tests.getAllResults().size() > 0) {
StringBuffer buff = new StringBuffer();
String lastClassName = "";
int mq = 0;
int cq = 0;
for (ITestNGMethod method : getMethodSet(tests, suite)) {
m_row += 1;
m_methodIndex += 1;
ITestClass testClass = method.getTestClass();
String className = testClass.getName();
if (mq == 0) {
String id = (m_testIndex == null ? null : "t"
+ Integer.toString(m_testIndex));
titleRow(testname + " — " + style + details, 5, id);
m_testIndex = null;
}
if (!className.equalsIgnoreCase(lastClassName)) {
if (mq > 0) {
cq += 1;
m_out.print("<tr class=\"" + style
+ (cq % 2 == 0 ? "even" : "odd") + "\">"
+ "<td");
if (mq > 1) {
m_out.print(" rowspan=\"" + mq + "\"");
}
m_out.println(">" + lastClassName + "</td>" + buff);
}
mq = 0;
buff.setLength(0);
lastClassName = className;
}
Set<ITestResult> resultSet = tests.getResults(method);
long end = Long.MIN_VALUE;
long start = Long.MAX_VALUE;
for (ITestResult testResult : tests.getResults(method)) {
if (testResult.getEndMillis() > end) {
end = testResult.getEndMillis();
}
if (testResult.getStartMillis() < start) {
start = testResult.getStartMillis();
}
}
mq += 1;
if (mq > 1) {
buff.append("<tr class=\"" + style
+ (cq % 2 == 0 ? "odd" : "even") + "\">");
}
String description = method.getDescription();
String testInstanceName = resultSet
.toArray(new ITestResult[] {})[0].getTestName();
buff.append("<td><a href=\"#m"
+ m_methodIndex
+ "\">"
+ qualifiedName(method)
+ " "
+ (description != null && description.length() > 0 ? "(\""
+ description + "\")"
: "")
+ "</a>"
+ (null == testInstanceName ? "" : "<br>("
+ testInstanceName + ")") + "</td>"
+ "<td class=\"numi\">" + resultSet.size() + "</td>"
+ "<td>" + start + "</td>" + "<td class=\"numi\">"
+ (end - start) + "</td>" + "</tr>");
}
if (mq > 0) {
cq += 1;
m_out.print("<tr class=\"" + style
+ (cq % 2 == 0 ? "even" : "odd") + "\">" + "<td");
if (mq > 1) {
m_out.print(" rowspan=\"" + mq + "\"");
}
m_out.println(">" + lastClassName + "</td>" + buff);
}
}
}
/** Starts and defines columns result summary table */
private void startResultSummaryTable(String style) {
tableStart(style, "summary");
m_out.println("<tr><th>Class</th>"
+ "<th>Method</th><th># of<br/>Scenarios</th><th>Start</th><th>Time<br/>(ms)</th></tr>");
m_row = 0;
}
private String qualifiedName(ITestNGMethod method) {
StringBuilder addon = new StringBuilder();
String[] groups = method.getGroups();
int length = groups.length;
if (length > 0 && !"basic".equalsIgnoreCase(groups[0])) {
addon.append("(");
for (int i = 0; i < length; i++) {
if (i > 0) {
addon.append(", ");
}
addon.append(groups[i]);
}
addon.append(")");
}
return "<b>" + method.getMethodName() + "</b> " + addon;
}
private void resultDetail(IResultMap tests) {
for (ITestResult result : tests.getAllResults()) {
ITestNGMethod method = result.getMethod();
m_methodIndex++;
String cname = method.getTestClass().getName();
m_out.println("<h2 id=\"m" + m_methodIndex + "\">" + cname + ":"
+ method.getMethodName() + "</h2>");
Set<ITestResult> resultSet = tests.getResults(method);
generateForResult(result, method, resultSet.size());
m_out.println("<p class=\"totop\"><a href=\"#summary\">back to summary</a></p>");
}
}
/**
* Write the first line of the stack trace
*
* @param tests
*/
private void getShortException(IResultMap tests) {
for (ITestResult result : tests.getAllResults()) {
m_methodIndex++;
Throwable exception = result.getThrowable();
List<String> msgs = Reporter.getOutput(result);
boolean hasReporterOutput = msgs.size() > 0;
boolean hasThrowable = exception != null;
if (hasThrowable) {
boolean wantsMinimalOutput = result.getStatus() == ITestResult.SUCCESS;
if (hasReporterOutput) {
m_out.print("<h3>"
+ (wantsMinimalOutput ? "Expected Exception"
: "Failure") + "</h3>");
}
// Getting first line of the stack trace
String str = Utils.stackTrace(exception, true)[0];
scanner = new Scanner(str);
String firstLine = scanner.nextLine();
m_out.println(firstLine);
}
}
}
/**
* Write all parameters
*
* @param tests
*/
private void getParameters(IResultMap tests) {
for (ITestResult result : tests.getAllResults()) {
m_methodIndex++;
Object[] parameters = result.getParameters();
boolean hasParameters = parameters != null && parameters.length > 0;
if (hasParameters) {
for (Object p : parameters) {
m_out.println(Utils.escapeHtml(Utils.toString(p)) + " | ");
}
}
}
}
private void generateForResult(ITestResult ans, ITestNGMethod method,
int resultSetSize) {
Object[] parameters = ans.getParameters();
boolean hasParameters = parameters != null && parameters.length > 0;
if (hasParameters) {
tableStart("result", null);
m_out.print("<tr class=\"param\">");
for (int x = 1; x <= parameters.length; x++) {
m_out.print("<th>Param." + x + "</th>");
}
m_out.println("</tr>");
m_out.print("<tr class=\"param stripe\">");
for (Object p : parameters) {
m_out.println("<td>" + Utils.escapeHtml(Utils.toString(p))
+ "</td>");
}
m_out.println("</tr>");
}
List<String> msgs = Reporter.getOutput(ans);
boolean hasReporterOutput = msgs.size() > 0;
Throwable exception = ans.getThrowable();
boolean hasThrowable = exception != null;
if (hasReporterOutput || hasThrowable) {
if (hasParameters) {
m_out.print("<tr><td");
if (parameters.length > 1) {
m_out.print(" colspan=\"" + parameters.length + "\"");
}
m_out.println(">");
} else {
m_out.println("<div>");
}
if (hasReporterOutput) {
if (hasThrowable) {
m_out.println("<h3>Test Messages</h3>");
}
for (String line : msgs) {
m_out.println(line + "<br/>");
}
}
if (hasThrowable) {
boolean wantsMinimalOutput = ans.getStatus() == ITestResult.SUCCESS;
if (hasReporterOutput) {
m_out.println("<h3>"
+ (wantsMinimalOutput ? "Expected Exception"
: "Failure") + "</h3>");
}
generateExceptionReport(exception, method);
}
if (hasParameters) {
m_out.println("</td></tr>");
} else {
m_out.println("</div>");
}
}
if (hasParameters) {
m_out.println("</table>");
}
}
protected void generateExceptionReport(Throwable exception,
ITestNGMethod method) {
m_out.print("<div class=\"stacktrace\">");
m_out.print(Utils.stackTrace(exception, true)[0]);
m_out.println("</div>");
}
/**
* Since the methods will be sorted chronologically, we want to return the
* ITestNGMethod from the invoked methods.
*/
private Collection<ITestNGMethod> getMethodSet(IResultMap tests,
ISuite suite) {
List<IInvokedMethod> r = Lists.newArrayList();
List<IInvokedMethod> invokedMethods = suite.getAllInvokedMethods();
for (IInvokedMethod im : invokedMethods) {
if (tests.getAllMethods().contains(im.getTestMethod())) {
r.add(im);
}
}
Arrays.sort(r.toArray(new IInvokedMethod[r.size()]), new TestSorter());
List<ITestNGMethod> result = Lists.newArrayList();
// Add all the invoked methods
for (IInvokedMethod m : r) {
result.add(m.getTestMethod());
}
// Add all the methods that weren't invoked (e.g. skipped) that we
// haven't added yet
for (ITestNGMethod m : tests.getAllMethods()) {
if (!result.contains(m)) {
result.add(m);
}
}
return result;
}
@SuppressWarnings("unused")
public void generateSuiteSummaryReport(List<ISuite> suites) {
tableStart("testOverview", null);
m_out.print("<tr>");
tableColumnStart("Test");
tableColumnStart("Methods<br/>Passed");
tableColumnStart("Scenarios<br/>Passed");
tableColumnStart("# skipped");
tableColumnStart("# failed");
tableColumnStart("Error messages");
tableColumnStart("Parameters");
tableColumnStart("Start<br/>Time");
tableColumnStart("End<br/>Time");
tableColumnStart("Total<br/>Time");
tableColumnStart("Included<br/>Groups");
tableColumnStart("Excluded<br/>Groups");
m_out.println("</tr>");
NumberFormat formatter = new DecimalFormat("#,##0.0");
int qty_tests = 0;
int qty_pass_m = 0;
int qty_pass_s = 0;
int qty_skip = 0;
int qty_fail = 0;
long time_start = Long.MAX_VALUE;
long time_end = Long.MIN_VALUE;
m_testIndex = 1;
for (ISuite suite : suites) {
if (suites.size() > 1) {
titleRow(suite.getName(), 8);
}
Map<String, ISuiteResult> tests = suite.getResults();
for (ISuiteResult r : tests.values()) {
qty_tests += 1;
ITestContext overview = r.getTestContext();
startSummaryRow(overview.getName());
int q = getMethodSet(overview.getPassedTests(), suite).size();
qty_pass_m += q;
summaryCell(q, Integer.MAX_VALUE);
q = overview.getPassedTests().size();
qty_pass_s += q;
summaryCell(q, Integer.MAX_VALUE);
q = getMethodSet(overview.getSkippedTests(), suite).size();
qty_skip += q;
summaryCell(q, 0);
q = getMethodSet(overview.getFailedTests(), suite).size();
qty_fail += q;
summaryCell(q, 0);
// NEW
// Insert error found
m_out.print("<td class=\"numi" + (true ? "" : "_attn") + "\">");
getShortException(overview.getFailedTests());
getShortException(overview.getSkippedTests());
m_out.println("</td>");
// NEW
// Add parameters for each test case (failed or passed)
m_out.print("<td class=\"numi" + (true ? "" : "_attn") + "\">");
// Write OS and Browser
// m_out.println(suite.getParameter("os").substring(0, 3) +
// " | "
// + suite.getParameter("browser").substring(0, 3) + " | ");
getParameters(overview.getFailedTests());
getParameters(overview.getPassedTests());
getParameters(overview.getSkippedTests());
m_out.println("</td>");
// NEW
summaryCell(
DateFunctions.dateToDayAndTime(overview.getStartDate()),
true);
m_out.println("</td>");
summaryCell(
DateFunctions.dateToDayAndTime(overview.getEndDate()),
true);
m_out.println("</td>");
time_start = Math.min(overview.getStartDate().getTime(),
time_start);
time_end = Math.max(overview.getEndDate().getTime(), time_end);
summaryCell(
formatter.format((overview.getEndDate().getTime() - overview
.getStartDate().getTime()) / 1000.)
+ " seconds", true);
summaryCell(overview.getIncludedGroups());
summaryCell(overview.getExcludedGroups());
m_out.println("</tr>");
m_testIndex++;
}
}
if (qty_tests > 1) {
m_out.println("<tr class=\"total\"><td>Total</td>");
summaryCell(qty_pass_m, Integer.MAX_VALUE);
summaryCell(qty_pass_s, Integer.MAX_VALUE);
summaryCell(qty_skip, 0);
summaryCell(qty_fail, 0);
summaryCell(" ", true);
summaryCell(" ", true);
summaryCell(" ", true);
summaryCell(" ", true);
summaryCell(
formatter.format(((time_end - time_start) / 1000.) / 60.)
+ " minutes", true);
m_out.println("<td colspan=\"3\"> </td></tr>");
}
m_out.println("</table>");
}
private void summaryCell(String[] val) {
StringBuffer b = new StringBuffer();
for (String v : val) {
b.append(v + " ");
}
summaryCell(b.toString(), true);
}
private void summaryCell(String v, boolean isgood) {
m_out.print("<td class=\"numi" + (isgood ? "" : "_attn") + "\">" + v
+ "</td>");
}
private void startSummaryRow(String label) {
m_row += 1;
m_out.print("<tr"
+ (m_row % 2 == 0 ? " class=\"stripe\"" : "")
+ "><td style=\"text-align:left;padding-right:2em\"><a href=\"#t"
+ m_testIndex + "\">" + label + "</a>" + "</td>");
}
private void summaryCell(int v, int maxexpected) {
summaryCell(String.valueOf(v), v <= maxexpected);
}
private void tableStart(String cssclass, String id) {
m_out.println("<table cellspacing=\"0\" cellpadding=\"0\""
+ (cssclass != null ? " class=\"" + cssclass + "\""
: " style=\"padding-bottom:2em\"")
+ (id != null ? " id=\"" + id + "\"" : "") + ">");
m_row = 0;
}
private void tableColumnStart(String label) {
m_out.print("<th>" + label + "</th>");
}
private void titleRow(String label, int cq) {
titleRow(label, cq, null);
}
private void titleRow(String label, int cq, String id) {
m_out.print("<tr");
if (id != null) {
m_out.print(" id=\"" + id + "\"");
}
m_out.println("><th colspan=\"" + cq + "\">" + label + "</th></tr>");
m_row = 0;
}
/** Starts HTML stream */
protected void startHtml(PrintWriter out) {
out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">");
out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
out.println("<head>");
out.println("<title>Hector Flores - TestNG Report</title>");
out.println("<style type=\"text/css\">");
out.println("table {margin-bottom:10px;border-collapse:collapse;empty-cells:show}");
out.println("td,th {border:1px solid #009;padding:.25em .5em}");
out.println(".result th {vertical-align:bottom}");
out.println(".param th {padding-left:1em;padding-right:1em}");
out.println(".param td {padding-left:.5em;padding-right:2em}");
out.println(".stripe td,.stripe th {background-color: #E6EBF9}");
out.println(".numi,.numi_attn {text-align:right}");
out.println(".total td {font-weight:bold}");
out.println(".passedodd td {background-color: #0A0}");
out.println(".passedeven td {background-color: #3F3}");
out.println(".skippedodd td {background-color: #CCC}");
out.println(".skippedodd td {background-color: #DDD}");
out.println(".failedodd td,.numi_attn {background-color: #F33}");
out.println(".failedeven td,.stripe .numi_attn {background-color: #D00}");
out.println(".stacktrace {white-space:pre;font-family:monospace}");
out.println(".totop {font-size:85%;text-align:center;border-bottom:2px solid #000}");
out.println("</style>");
out.println("</head>");
out.println("<body>");
}
/** Finishes HTML stream */
protected void endHtml(PrintWriter out) {
out.println("<center> Report customized by Hector Flores [[email protected]] </center>");
out.println("</body></html>");
}
// ~ Inner Classes --------------------------------------------------------
/** Arranges methods by classname and method name */
private class TestSorter implements Comparator<IInvokedMethod> {
// ~ Methods
// -------------------------------------------------------------
/** Arranges methods by classname and method name */
@Override
public int compare(IInvokedMethod o1, IInvokedMethod o2) {
// System.out.println("Comparing " + o1.getMethodName() + " " +
// o1.getDate()
// + " and " + o2.getMethodName() + " " + o2.getDate());
return (int) (o1.getDate() - o2.getDate());
// int r = ((T) o1).getTestClass().getName().compareTo(((T)
// o2).getTestClass().getName());
// if (r == 0) {
// r = ((T) o1).getMethodName().compareTo(((T) o2).getMethodName());
// }
// return r;
}
}
}
With those steps you already have your listener ready to listen.
How to call it?
If you use testng.xml add the following lines:
<listeners>
<listener class-name='[your_class_path].MyReporterListener'/>
</listeners>
If you run your tests from a java class, add the following lines:
private final static MyReporterListener frl = new MyReporterListener();
TestNG testng = new TestNG();
testng.addListener(frl);
With those steps, when you execute your tests you'll have two emailable reports, customized and original.
Now it's time to pimp your report.
In my case I had to add error messages, parameters and times (start and end), because it's very useful if you want to paste on an excel file.
My customized report:
You have to mainly modify generateSuiteSummaryReport(List suites) function.
Play with that and ask me if you have any problem.
这篇关于自定义 TestNG 可发送电子邮件报告的摘要部分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
08-23 18:33