问题 如何编写Jenkins email-ext模板来显示标准测试报告等测试结果


我已经调整了标准果冻模板以在表格中显示当前测试结果,但是我真的希望能够显示差异,如Jenkins自己的测试结果页面所示。

例如:

JUnit Tests: 0 failures (±0) , 1 skipped (+1)

Package               Duration   Fail  (diff)  Skip  (diff)  Total  (diff)
foo.bar.baz              89 ms      0      0     1       +1     5       +2

3585
2017-07-04 16:51


起源



答案:


为Email Ext插件而不是Jelly模板编写Groovy模板。在Groovy模板中,您可以访问 建立 你的构建对象。然后你可以打电话 getTestResultAction 在它上面获得 AbstractTestResultAction 对于构建,您可以查询所需的一切。

这是一个链接 Jenkins主模块API。可以在中找到Ext Email插件的示例Groovy模板 $JENKINS_HOME/plugins/email-ext/WEB-INF/classes/hudson/plugins/emailext/templates/groovy-html.template。有关Groovy模板/脚本用法的更多信息,请参阅 电子邮件Ext插件文档


9
2017-07-05 20:24



@Jon,这个答案有用吗? - malenkiy_scot
我已经设法使用build.testResultAction.failedTests来取消失败的测试,但我不知道如何访问所有测试 - Jon Freedman
看起来访问PackageResult取决于getTestResultAction返回的类型 - AggregatedTestResultAction和TestResultAction需要不同的处理。 - Jon Freedman
@Jon,你能分享一下你的Groovy代码吗? - Josh Unger
这是一个 链接到groovy-html.template。


答案:


为Email Ext插件而不是Jelly模板编写Groovy模板。在Groovy模板中,您可以访问 建立 你的构建对象。然后你可以打电话 getTestResultAction 在它上面获得 AbstractTestResultAction 对于构建,您可以查询所需的一切。

这是一个链接 Jenkins主模块API。可以在中找到Ext Email插件的示例Groovy模板 $JENKINS_HOME/plugins/email-ext/WEB-INF/classes/hudson/plugins/emailext/templates/groovy-html.template。有关Groovy模板/脚本用法的更多信息,请参阅 电子邮件Ext插件文档


9
2017-07-05 20:24



@Jon,这个答案有用吗? - malenkiy_scot
我已经设法使用build.testResultAction.failedTests来取消失败的测试,但我不知道如何访问所有测试 - Jon Freedman
看起来访问PackageResult取决于getTestResultAction返回的类型 - AggregatedTestResultAction和TestResultAction需要不同的处理。 - Jon Freedman
@Jon,你能分享一下你的Groovy代码吗? - Josh Unger
这是一个 链接到groovy-html.template。


如果您在如何通过内部API访问它(很难知道并且始终存在限制),那么还有另一种更灵活的方法。

使用FILE标记而不是groovy模板

  1. 使用脚本通过访问您的测试数据 詹金斯API,对于你的情况,它就像 HTTP://jenkins.server/job/yourjob/lastCompletedBuild/testReport/api/xml 并生成自己的html文件,如 email.html 在工作区下
  2. Default Content 在email-ext配置中使用FILE令牌直接发送电子邮件 ${FILE, path="email.html"}

在上面的步骤1中,您还可以使用更灵活的方式为您自己的模板,我使用python脚本和简单的字符串模板。

它对我来说很完美。


3
2017-12-08 14:22



您是否愿意分享您在步骤1中描述的脚本? - callisto
如果您使用HTML格式,则需要使用 ${SCRIPT, template="groovy-html.template"} 代替 ${FILE}。
能不能请详细说明这个。我想我需要做类似的事情。我想在电子邮件中显示来自git的完整git commit消息。但它只显示第一行。我想我需要写一些脚本.. - sharp


要扩展这个答案: 为Email Ext插件而不是Jelly模板编写Groovy模板。 在可编辑的电子邮件通知内容中

  • 将内容类型设置为“HTML”或“HTML和纯文本”
  • 并包括像这样的groovy脚本:

    $ {SCRIPT,template =“test.groovy”}

  • 将groovy脚本放在电子邮件模板中,例如 在/ var / lib中/詹金斯/电子邮件模板。见下面的test.groovy。

在下面的示例中,通过获取每个对象来迭代每个测试:'''junitResult.getChildren()'''。如果希望只迭代失败的测试,则可以使用junitResult.getFailedTests()。请参阅hudson.tasks.junit.TestResult API: http://hudson-ci.org/javadoc/hudson/tasks/junit/PackageResult.html  也看到了 http://hudson-ci.org/javadoc/hudson/model/Build.html

Collection<ClassResult> getChildren()
List<CaseResult>    getFailedTests()

来自email-ext-plugin的示例/模板可以在这里看到: https://github.com/jenkinsci/email-ext-plugin/blob/master/src/main/resources/hudson/plugins/emailext/templates/groovy-html.template

此示例显示每个测试套件和单个测试的结果的摘要测试结果和表。 test.groovy:

<html>
<body>
<%

    import hudson.model.*

    def build = Thread.currentThread().executable
    def buildNumber = build.number
    def buildNumHash = build.getDisplayName()

    def testCount = "0"
    def testPassed = "0"
    def testFailed = "0"
    def testSkipped = "0"
    def buildDuration = "0"
    if(build.testResultAction) {
        def testResult = build.testResultAction
        testCount = String.format("%d",(testResult.totalCount))
        testPassed = String.format("%d",(testResult.result.passCount))
        testFailed = String.format("%d",(testResult.result.failCount))
        testSkipped = String.format("%d",(testResult.result.skipCount))
        testDuration = String.format("%.2f",(testResult.result.duration ))
    }

    def workspace = build.getEnvVars()["WORKSPACE"]
    def buildName = build.getEnvVars()["JOB_NAME"]
    def BUILD_STATUS = build.getEnvVars()["BUILD_STATUS"]
    def BUILD_URL = build.getEnvVars()["BUILD_URL"]

    def testResult = hudson.tasks.junit.TestResult

    def testResult2 = build.getAction(hudson.tasks.junit.TestResultAction.class)

%>

start test.groovy <br><br>
<b>TEST RESULT:</b> $testCount total, <b>$testPassed pass</b>, <b>$testFailed fail</b>, $testSkipped skip.<br>
Workspace : $workspace<br>
Project Name : $buildName $buildNumHash<br><br>

<!-- GENERAL INFO -->

<TABLE>
  <TR><TD align="right">
    <j:choose>
      <j:when test="${build.result=='SUCCESS'}">
        <IMG SRC="${rooturl}static/e59dfe28/images/32x32/blue.gif" />
      </j:when>
          <j:when test="${build.result=='FAILURE'}">
        <IMG SRC="${rooturl}static/e59dfe28/images/32x32/red.gif" />
      </j:when>
      <j:otherwise>
        <IMG SRC="${rooturl}static/e59dfe28/images/32x32/yellow.gif" />
      </j:otherwise>
    </j:choose>
  </TD><TD valign="center"><B style="font-size: 200%;">BUILD ${build.result}</B></TD></TR>
  <TR><TD>Build URL</TD><TD><A href="${rooturl}${build.url}">${rooturl}${build.url}</A></TD></TR>
  <TR><TD>Project:</TD><TD>${project.name}</TD></TR>
  <TR><TD>Date of build:</TD><TD>${it.timestampString}</TD></TR>
  <TR><TD>Build duration:</TD><TD>${build.durationString}</TD></TR>
  <TR><TD>Test duration:</TD><TD>${testDuration}</TD></TR>
</TABLE>
<BR/>

<!-- JUnit TEMPLATE  hudson.tasks.junit.TestResult   -->

<% def junitResultList = it.JUnitTestResult
try {
 def cucumberTestResultAction = it.getAction("org.jenkinsci.plugins.cucumber.jsontestsupport.CucumberTestResultAction")
 junitResultList.add(cucumberTestResultAction.getResult())
} catch(e) {
        //cucumberTestResultAction not exist in this build
}
// API: http://hudson-ci.org/javadoc/hudson/tasks/junit/PackageResult.html
%>

<!-- JUnit TEMPLATE: all tests PASS FAIL SKIP >
<% 
if (junitResultList.size() > 0) { %>
 <TABLE width="100%">
 <TR><TD class="bg1" colspan="2"><B>${junitResultList.first().displayName}</B></TD></TR>
 <% junitResultList.each{
  junitResult -> %>
     <% junitResult.getChildren().each { packageResult -> %>
        <TR><TD class="bg2" colspan="2"> <B>TEST SUITE: ${packageResult.getName()} Failed: ${packageResult.getFailCount()} test(s), Passed: ${packageResult.getPassCount()} test(s)</B>, Skipped: ${packageResult.getSkipCount()} test(s), Total: ${packageResult.getPassCount()+packageResult.getFailCount()+packageResult.getSkipCount()} test(s)</TD></TR>
        <% packageResult.getChildren().each{ suite -> 
               suite.getChildren().each{ test ->
           def colour = "lightgreen"
           def highlight1=""
           def highlight2=""
           RESULT = test.getStatus() // FAILED or PASSED or SKIPPED
           if (RESULT == hudson.tasks.junit.CaseResult.Status.FAILED || RESULT == hudson.tasks.junit.CaseResult.Status.REGRESSION) {
               colour = "#ffcccc" 
               highlight1="<B>"
               highlight2="</B>"
           }
           if (RESULT == hudson.tasks.junit.CaseResult.Status.SKIPPED) { colour = "#ffffb3" }
         %>
          <TR bgcolor="${colour}"><TD class="test" colspan="2">${highlight1}<li>${RESULT}: ${test.getFullName()} </li>${highlight2}</TD></TR>
        <% } }
      }
 } %>
 </TABLE>
 <BR/>
<%
} %>

end of test.groovy

</body>
</html>

例如输出(文本只有颜色/格式)

start test.groovy 

TEST RESULT: 18 total, 18 pass, 0 fail, 0 skip. 
Workspace : /var/lib/jenkins/jobs/jobname-1/workspace 
Project Name : jobname-1 #20

BUILD SUCCESS 

Build URL   http://jenkinsurl:port/job/jobname-1/20/
Project:    jobname-1 
Date of build:  Mon, 23 Jan 2017 09:29:00 +0000 
Build duration: 10 min 
Test duration:  267.12

Test Results 
TEST SUITE: suitename1 Failed: 0 test(s), Passed: 3 test(s), Skipped: 0 test(s), Total: 3 test(s) 
 * PASSED: suitename1.testclass.testname1
 * PASSED: suitename1.testclass.testname2
 * PASSED: suitename1.testclass.testname3
TEST SUITE: suitename2 Failed: 2 test(s), Passed: 1 test(s), Skipped: 0 test(s), Total: 3 test(s) 
 * PASSED: suitename2.testclass.testname1
 * FAILED: suitename2.testclass.testname2
 * REGRESSION: suitename2.testclass.testname3

end of test.groovy

3
2018-01-23 10:16





我在Jelly中的解决方案基于默认的static-analysys.jelly脚本

  <!-- JUnit TEMPLATE -->
  <j:set var="junitResultList" value="${it.JUnitTestResult}" />
  <j:if test="${junitResultList.isEmpty()!=true}">
    <div class="content">
      <a href="${rooturl}${build.url}/testReport">
        <h1>JUnit Tests</h1>
      </a>
      <table class="border">
        <tr>
          <th class="border">Package</th>
          <th class="border">Failed</th>
          <th class="border">Failed (diff)</th>
          <th class="border">Passed</th>
          <th class="border">Passed (diff)</th>
          <th class="border">Skipped</th>
          <th class="border">Skipped (diff)</th>
          <th class="border">Total</th>
          <th class="border">Total (diff)</th>
        </tr>
        <j:forEach var="junitResult" items="${it.JUnitTestResult}">
          <j:forEach var="packageResult" items="${junitResult.getChildren()}">
            <tr>
              <td class="border">
                <tt>${packageResult.getName()}</tt>
              </td>
              <td class="border test_failed">${packageResult.getFailCount()}</td>
              <td class="border test_failed">${packageResult.getFailCount()-packageResult.previousResult.getFailCount()}</td>
              <td class="border test_passed">${packageResult.getPassCount()}</td>
              <td class="border test_passed">${packageResult.getPassCount()-packageResult.previousResult.getPassCount()}</td>
              <td class="border test_skipped">${packageResult.getSkipCount()}</td>
              <td class="border test_skipped">${packageResult.getSkipCount()-packageResult.previousResult.getSkipCount()}</td>
              <td class="border">
                <b>${packageResult.getPassCount()+packageResult.getFailCount()+packageResult.getSkipCount()}
                </b>
              </td>
              <td class="border">
                <b>${packageResult.getPassCount()+packageResult.getFailCount()+packageResult.getSkipCount()-packageResult.previousResult.getPassCount()-packageResult.previousResult.getFailCount()-packageResult.previousResult.getSkipCount()}
                </b>
              </td>
            </tr>
            <j:forEach var="failed_test"
              items="${packageResult.getFailedTests()}">
              <tr>
                <td class="test_failed" colspan="5">
                  <tt>${failed_test.getFullName()}</tt>
                </td>
              </tr>
            </j:forEach>
          </j:forEach>
        </j:forEach>
      </table>
      <br />
    </div>
  </j:if>

1
2018-03-06 10:08