如何访问 Jenkins Pipeline 项目中的 Junit 测试计数
How to access Junit test counts in Jenkins Pipeline project
我刚开始使用 Jenkins
我的自由式项目曾经像这样在 Slack 中报告 JUnit 测试结果
MyJenkinsFreestyle - #79 Unstable after 4 min 59 sec (Open)
Test Status:
Passed: 2482, Failed: 13, Skipped: 62
现在我已将其移至管道项目,一切都很好,除了 Slack 通知没有测试状态
done MyPipelineProject #68 UNSTABLE
我知道我必须构建要发送给 Slack 的消息,我现在已经在上面完成了。
唯一的问题是如何读取测试状态 - 通过计数、失败计数等。
这在 Jenkins slack-plugin commit 中称为 "test summary",这是屏幕截图
那么如何在 Jenkins Pipeline 项目中访问 Junit 测试 count/details? - 以便在通知中报告这些内容。
更新:
在 Freestyle 项目中,Slack 通知本身就有 "test summary",并且没有选择(或不选择)测试摘要的选项。
在 Pipeline 项目中,我对 "Publish JUnit test results" 的 "junit" 命令是在发送 Slack 通知之前。
所以在代码中这些行看起来像这样(这是最后阶段的最后几行):
bat runtests.bat
junit 'junitreport/xml/TEST*.xml'
slackSend channel: '#testschannel', color: 'normal', message: "done ${env.JOB_NAME} ${env.BUILD_NUMBER} (<${env.BUILD_URL}|Open>)";
从 Cloudbees 的 this presentation 我发现应该可以通过 "build" 对象。
它有像
这样的代码
def testResult = build.testResultAction
def total = testResult.totalCount
但是 currentBuild 不提供对 testResultAction 的访问。
所以继续搜索并找到了这个 post "react on failed tests in pipeline script"。
罗伯特·桑德尔 (Robert Sandell) 给出了 "pro tip"
Pro tip, requires some "custom whitelisting":
AbstractTestResultAction testResultAction = currentBuild.rawBuild.getAction(AbstractTestResultAction.class)
if (testResultAction != null) {
echo "Tests: ${testResultAction.failCount} / ${testResultAction.failureDiffString} failures of ${testResultAction.totalCount}.\n\n"
}
这就像一个魅力 - 只是我不得不取消选择 "Groovy sandbox" 复选框。
现在我在构建日志中有这些
Tests: 11 / ±0 failures of 2624
现在我将使用它来准备字符串以在 slack 中通知测试结果。
更新:
最后,我用来获取如下输出的函数
(注意测试失败后的 "failure diff" 非常有用)
Test Status:
Passed: 2628, Failed: 6 / ±0, Skipped: 0
如下:
import hudson.tasks.test.AbstractTestResultAction
@NonCPS
def testStatuses() {
def testStatus = ""
AbstractTestResultAction testResultAction = currentBuild.rawBuild.getAction(AbstractTestResultAction.class)
if (testResultAction != null) {
def total = testResultAction.totalCount
def failed = testResultAction.failCount
def skipped = testResultAction.skipCount
def passed = total - failed - skipped
testStatus = "Test Status:\n Passed: ${passed}, Failed: ${failed} ${testResultAction.failureDiffString}, Skipped: ${skipped}"
if (failed == 0) {
currentBuild.result = 'SUCCESS'
}
}
return testStatus
}
更新 2018-04-19
请注意,以上内容需要手动 "whitelisting" 使用的方法。
这是一次性将所有方法列入白名单的方法
手动更新白名单...
退出詹金斯
Create/Update %USERPROFILE%.jenkins\scriptApproval.xml 具有以下内容
<?xml version='1.0' encoding='UTF-8'?>
<scriptApproval plugin="script-security@1.23">
<approvedScriptHashes>
</approvedScriptHashes>
<approvedSignatures>
<string>method hudson.model.Actionable getAction java.lang.Class</string>
<string>method hudson.model.Cause getShortDescription</string>
<string>method hudson.model.Run getCauses</string>
<string>method hudson.tasks.test.AbstractTestResultAction getFailCount</string>
<string>method hudson.tasks.test.AbstractTestResultAction getFailureDiffString</string>
<string>method hudson.tasks.test.AbstractTestResultAction getSkipCount</string>
<string>method hudson.tasks.test.AbstractTestResultAction getTotalCount</string>
<string>method org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper getRawBuild</string>
</approvedSignatures>
<aclApprovedSignatures/>
<approvedClasspathEntries/>
<pendingScripts/>
<pendingSignatures/>
<pendingClasspathEntries/>
</scriptApproval>
- 重启詹金斯
- 然后验证 "In script approval" 是否已批准上述条目
- 注意:哪个很重要。所以如果scriptApproval文件已经存在,那么一般需要确保tag的内容。
为了扩展@vikramsjn 的回答,这是我用来在我的 Jenkinsfile 中获取测试摘要的内容:
import hudson.tasks.test.AbstractTestResultAction
import hudson.model.Actionable
@NonCPS
def getTestSummary = { ->
def testResultAction = currentBuild.rawBuild.getAction(AbstractTestResultAction.class)
def summary = ""
if (testResultAction != null) {
def total = testResultAction.getTotalCount()
def failed = testResultAction.getFailCount()
def skipped = testResultAction.getSkipCount()
summary = "Test results:\n\t"
summary = summary + ("Passed: " + (total - failed - skipped))
summary = summary + (", Failed: " + failed)
summary = summary + (", Skipped: " + skipped)
} else {
summary = "No tests found"
}
return summary
}
然后我使用此方法实例化我的 testSummary
变量:
def testSummary = getTestSummary()
这将 return 类似于:
"Test results:
Passed: 123, Failed: 0, Skipped: 0"
首先感谢楼上的回答。他们为我节省了很多时间,我在我的管道中使用了建议的解决方案。但是我没有使用 "whitelisting" 并且它工作正常。
我为 Jenkins 管道使用共享库,这里是该共享库的一部分,带有管道并使用方法获取计数:
import hudson.model.*
import jenkins.model.*
import hudson.tasks.test.AbstractTestResultAction
def call(Closure body) {
...
def emailTestReport = ""
pipeline {
...
stages{
stage('Test'){
...
post {
always {
junit 'tests.xml'
script {
AbstractTestResultAction testResultAction = currentBuild.rawBuild.getAction(AbstractTestResultAction.class)
if (testResultAction != null) {
def totalNumberOfTests = testResultAction.totalCount
def failedNumberOfTests = testResultAction.failCount
def failedDiff = testResultAction.failureDiffString
def skippedNumberOfTests = testResultAction.skipCount
def passedNumberOfTests = totalNumberOfTests - failedNumberOfTests - skippedNumberOfTests
emailTestReport = "Tests Report:\n Passed: ${passedNumberOfTests}; Failed: ${failedNumberOfTests} ${failedDiff}; Skipped: ${skippedNumberOfTests} out of ${totalNumberOfTests} "
}
}
mail to: 'example@email.com',
subject: "Tests are finished: ${currentBuild.fullDisplayName}",
body: "Tests are finished ${env.BUILD_URL}\n Test Report: ${emailTestReport} "
}
}
}
}
}
}
p.s。如果我在脚本 "section" 中创建 emailTestRepot 作为局部变量,我会得到下一个异常:
an exception which occurred:
in field locals
in field parent
in field caller
in field e
in field program
in field threads
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@11cd92de
Caused: java.io.NotSerializableException: hudson.tasks.junit.TestResultAction
...
我在尝试解决这个问题时遇到了很多困难 java.io.NotSerializableException。据我了解,我需要使用 "whitelisting" 来防止 NotSerializableException。但我真的不想这样做,当我将 "def emailTestReport" 移出管道时,它工作得很好。
对于 2020 年来到这里的任何人来说,现在似乎有一种更简单的方法。调用'junit testResults' returns一个TestResultSummary对象,可以赋值给一个变量,以后使用。
以通过 slack 发送摘要为例:
def summary = junit testResults: '/somefolder/*-reports/TEST-*.xml'
slackSend (
channel: "#mychannel",
color: '#007D00',
message: "\n *Test Summary* - ${summary.totalCount}, Failures: ${summary.failCount}, Skipped: ${summary.skipCount}, Passed: ${summary.passCount}"
)
我刚开始使用 Jenkins
我的自由式项目曾经像这样在 Slack 中报告 JUnit 测试结果
MyJenkinsFreestyle - #79 Unstable after 4 min 59 sec (Open)
Test Status:
Passed: 2482, Failed: 13, Skipped: 62
现在我已将其移至管道项目,一切都很好,除了 Slack 通知没有测试状态
done MyPipelineProject #68 UNSTABLE
我知道我必须构建要发送给 Slack 的消息,我现在已经在上面完成了。
唯一的问题是如何读取测试状态 - 通过计数、失败计数等。
这在 Jenkins slack-plugin commit 中称为 "test summary",这是屏幕截图
那么如何在 Jenkins Pipeline 项目中访问 Junit 测试 count/details? - 以便在通知中报告这些内容。
更新: 在 Freestyle 项目中,Slack 通知本身就有 "test summary",并且没有选择(或不选择)测试摘要的选项。
在 Pipeline 项目中,我对 "Publish JUnit test results" 的 "junit" 命令是在发送 Slack 通知之前。
所以在代码中这些行看起来像这样(这是最后阶段的最后几行):
bat runtests.bat
junit 'junitreport/xml/TEST*.xml'
slackSend channel: '#testschannel', color: 'normal', message: "done ${env.JOB_NAME} ${env.BUILD_NUMBER} (<${env.BUILD_URL}|Open>)";
从 Cloudbees 的 this presentation 我发现应该可以通过 "build" 对象。 它有像
这样的代码def testResult = build.testResultAction
def total = testResult.totalCount
但是 currentBuild 不提供对 testResultAction 的访问。
所以继续搜索并找到了这个 post "react on failed tests in pipeline script"。 罗伯特·桑德尔 (Robert Sandell) 给出了 "pro tip"
Pro tip, requires some "custom whitelisting":
AbstractTestResultAction testResultAction = currentBuild.rawBuild.getAction(AbstractTestResultAction.class) if (testResultAction != null) { echo "Tests: ${testResultAction.failCount} / ${testResultAction.failureDiffString} failures of ${testResultAction.totalCount}.\n\n" }
这就像一个魅力 - 只是我不得不取消选择 "Groovy sandbox" 复选框。 现在我在构建日志中有这些
Tests: 11 / ±0 failures of 2624
现在我将使用它来准备字符串以在 slack 中通知测试结果。
更新:
最后,我用来获取如下输出的函数 (注意测试失败后的 "failure diff" 非常有用)
Test Status:
Passed: 2628, Failed: 6 / ±0, Skipped: 0
如下:
import hudson.tasks.test.AbstractTestResultAction
@NonCPS
def testStatuses() {
def testStatus = ""
AbstractTestResultAction testResultAction = currentBuild.rawBuild.getAction(AbstractTestResultAction.class)
if (testResultAction != null) {
def total = testResultAction.totalCount
def failed = testResultAction.failCount
def skipped = testResultAction.skipCount
def passed = total - failed - skipped
testStatus = "Test Status:\n Passed: ${passed}, Failed: ${failed} ${testResultAction.failureDiffString}, Skipped: ${skipped}"
if (failed == 0) {
currentBuild.result = 'SUCCESS'
}
}
return testStatus
}
更新 2018-04-19
请注意,以上内容需要手动 "whitelisting" 使用的方法。 这是一次性将所有方法列入白名单的方法
手动更新白名单...
退出詹金斯
Create/Update %USERPROFILE%.jenkins\scriptApproval.xml 具有以下内容
<?xml version='1.0' encoding='UTF-8'?>
<scriptApproval plugin="script-security@1.23">
<approvedScriptHashes>
</approvedScriptHashes>
<approvedSignatures>
<string>method hudson.model.Actionable getAction java.lang.Class</string>
<string>method hudson.model.Cause getShortDescription</string>
<string>method hudson.model.Run getCauses</string>
<string>method hudson.tasks.test.AbstractTestResultAction getFailCount</string>
<string>method hudson.tasks.test.AbstractTestResultAction getFailureDiffString</string>
<string>method hudson.tasks.test.AbstractTestResultAction getSkipCount</string>
<string>method hudson.tasks.test.AbstractTestResultAction getTotalCount</string>
<string>method org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper getRawBuild</string>
</approvedSignatures>
<aclApprovedSignatures/>
<approvedClasspathEntries/>
<pendingScripts/>
<pendingSignatures/>
<pendingClasspathEntries/>
</scriptApproval>
- 重启詹金斯
- 然后验证 "In script approval" 是否已批准上述条目
- 注意:哪个很重要。所以如果scriptApproval文件已经存在,那么一般需要确保tag的内容。
为了扩展@vikramsjn 的回答,这是我用来在我的 Jenkinsfile 中获取测试摘要的内容:
import hudson.tasks.test.AbstractTestResultAction
import hudson.model.Actionable
@NonCPS
def getTestSummary = { ->
def testResultAction = currentBuild.rawBuild.getAction(AbstractTestResultAction.class)
def summary = ""
if (testResultAction != null) {
def total = testResultAction.getTotalCount()
def failed = testResultAction.getFailCount()
def skipped = testResultAction.getSkipCount()
summary = "Test results:\n\t"
summary = summary + ("Passed: " + (total - failed - skipped))
summary = summary + (", Failed: " + failed)
summary = summary + (", Skipped: " + skipped)
} else {
summary = "No tests found"
}
return summary
}
然后我使用此方法实例化我的 testSummary
变量:
def testSummary = getTestSummary()
这将 return 类似于:
"Test results:
Passed: 123, Failed: 0, Skipped: 0"
首先感谢楼上的回答。他们为我节省了很多时间,我在我的管道中使用了建议的解决方案。但是我没有使用 "whitelisting" 并且它工作正常。 我为 Jenkins 管道使用共享库,这里是该共享库的一部分,带有管道并使用方法获取计数:
import hudson.model.*
import jenkins.model.*
import hudson.tasks.test.AbstractTestResultAction
def call(Closure body) {
...
def emailTestReport = ""
pipeline {
...
stages{
stage('Test'){
...
post {
always {
junit 'tests.xml'
script {
AbstractTestResultAction testResultAction = currentBuild.rawBuild.getAction(AbstractTestResultAction.class)
if (testResultAction != null) {
def totalNumberOfTests = testResultAction.totalCount
def failedNumberOfTests = testResultAction.failCount
def failedDiff = testResultAction.failureDiffString
def skippedNumberOfTests = testResultAction.skipCount
def passedNumberOfTests = totalNumberOfTests - failedNumberOfTests - skippedNumberOfTests
emailTestReport = "Tests Report:\n Passed: ${passedNumberOfTests}; Failed: ${failedNumberOfTests} ${failedDiff}; Skipped: ${skippedNumberOfTests} out of ${totalNumberOfTests} "
}
}
mail to: 'example@email.com',
subject: "Tests are finished: ${currentBuild.fullDisplayName}",
body: "Tests are finished ${env.BUILD_URL}\n Test Report: ${emailTestReport} "
}
}
}
}
}
}
p.s。如果我在脚本 "section" 中创建 emailTestRepot 作为局部变量,我会得到下一个异常:
an exception which occurred:
in field locals
in field parent
in field caller
in field e
in field program
in field threads
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@11cd92de
Caused: java.io.NotSerializableException: hudson.tasks.junit.TestResultAction
...
我在尝试解决这个问题时遇到了很多困难 java.io.NotSerializableException。据我了解,我需要使用 "whitelisting" 来防止 NotSerializableException。但我真的不想这样做,当我将 "def emailTestReport" 移出管道时,它工作得很好。
对于 2020 年来到这里的任何人来说,现在似乎有一种更简单的方法。调用'junit testResults' returns一个TestResultSummary对象,可以赋值给一个变量,以后使用。
以通过 slack 发送摘要为例:
def summary = junit testResults: '/somefolder/*-reports/TEST-*.xml'
slackSend (
channel: "#mychannel",
color: '#007D00',
message: "\n *Test Summary* - ${summary.totalCount}, Failures: ${summary.failCount}, Skipped: ${summary.skipCount}, Passed: ${summary.passCount}"
)