RobotFramework与Jenkins集成发送邮件

转:

A.    目标:
实现RobotFramework的脚本定时自动执行,执行完后自动将结果发送到指定邮箱

B.    前提
1、 配置好Robot Framework的环境,脚本可以正常运行

2、 部署好Jenkins的环境,Jenkins的安装不是本文的重点,不懂的请问度娘(其实很简单,装Tomcat,把Jenkins.war包扔到Tomcat的webapp目录里)

3、 在Jenkins里安装好以下插件:EmailExtension Plugin、Zentimestamp plugin、Robot Framework plugin

C.     安装相关插件
Jenkins安装Email Extension Plugin、Zentimestampplugin、Robot Framework plugin插件

在线直接安装插件:
在“可选插件”搜索Email Extension Plugin、Zentimestamp plugin、Robot Frameworkplugin插件名,选择之后,点击直接安装

 RobotFramework与Jenkins集成发送邮件

注:建议用在线直接安装,因为插件与插件中存在相互依赖性,手动处理会很麻烦,但是直接安装,系统可以直接处理依赖关系

离线安装插件:(不建议使用)
1)     下载插件(由于无法在线安装,所以只能走离线安装)
首先到网址http://updates.jenkins-ci.org/download/plugins/中下载了Email Extension Plugin、Zentimestampplugin、Robot Framework plugin插件:

 RobotFramework与Jenkins集成发送邮件

2)     进入安装插件界面
点击 系统管理——>管理插件——>高级

 RobotFramework与Jenkins集成发送邮件

这时候我们是不是看到了中间有个上传插件的地方(其他地方我们的信息不一样可以不管)。

点击选择文件按钮,选中刚才我们下载的插件,注意每次只能选中一个文件,所以先选择依赖文件。然后点击上传按钮。这样依次就可以将所有的插件离线安装成功了!如下图所示:

D.    配置
1.   系统设置
1)     进入【系统管理】-【系统设置】进行如下配置:设置${BUILD_TIMESTAMP}格式

RobotFramework与Jenkins集成发送邮件


2)     配置 ExtendedE-mail Notification默认设置
设置邮件内容和发送人:

default content type:设置邮件发送的格式:文本格式或者html格式

Use List-ID Email Header:设置邮件的发送的名称(便于过滤)

Default Recipients:设置默认的收件人

Reply To List:设置默认回复列表

Emergency reroute:相当于一个邮件的转发(邮件先发送到这里,然后在进行进一步处理)

Excluded Recipients:设置接收的黑名单(就是不发送给这些人)

Default Subject:设置默认的邮件主题

Maximum Attachment Size:这只邮件附件的最大值

Default Content:设置邮件的默认内容(里面可以引用一些环境变量的参数,或者插件的一些变量)

Default Pre-send Script:在发送邮件前执行的脚本

邮件通知:这是默认的邮件发送工具,配置和extemail插件设置差不多,只是不能设置邮件的默认发送策略,和邮件发送的内容等信息

 RobotFramework与Jenkins集成发送邮件

【user name:认证的邮箱;password:认证的密码(并非邮箱的密码,是开启smtp时给的一串字符);如果邮箱是ssl链接,就需要勾选use ssl;smtp port :如果你的smtp服务不是465的端口,需要配置对应的端口;charset:邮件编码设置】

注:这里的密码并不是我们邮箱真正的密码,这是QQ邮箱为了给第三方客户端登录的授权码,QQ邮箱是默认关闭SMTP的,所以我们还需要去开通QQ邮箱的SMTP服务,否则第三方客户端无法正常发送邮件。

QQ邮箱开通的SMTP服务步骤如下:

 RobotFramework与Jenkins集成发送邮件

RobotFramework与Jenkins集成发送邮件

根据提示发送信息,之后QQ会给一个授权码,把该授权码,填到Extended E-mail Notification的密码即可

 RobotFramework与Jenkins集成发送邮件

Extended E-mail Notification默认设置里Default Content的值是填写 ${SCRIPT,template=”robot_results.groovy”}设置这个模板:

在$Jenkins_Home/email-templates目录(如果没有email-templates请自行创建)下创建一个robot_results.groovy文件,内容如下:

robot_results.groovy文件下载地址:https://download.csdn.net/download/glongljl/10396246

其中Jenkins_Home的路径不知道在哪里的话,你可以去看一下系统设置页面,上面有写有:

 RobotFramework与Jenkins集成发送邮件

2.   创建任务

 RobotFramework与Jenkins集成发送邮件

RobotFramework与Jenkins集成发送邮件


3.   任务的配置
1)     General
选中“Restrict where this project can be run”,其LabelExpression填写“master”

 RobotFramework与Jenkins集成发送邮件

2)     源码管理

RobotFramework与Jenkins集成发送邮件


3)     构建触发器

RobotFramework与Jenkins集成发送邮件


4)     构建环境

RobotFramework与Jenkins集成发送邮件


5)     构建
如果在Windows系统中搭建jenkins的话,在构建中,选择“Execute Windows batch command”,输入pybot.bat  d: est.txt (这只是举个例子,具体执行哪个目录下的哪些case,根据实际情况决定)

 RobotFramework与Jenkins集成发送邮件

如果不清楚pybot.bat的用法,可以用RIDE跑一个用例,看command信息,先直接拿过来调试用用

 RobotFramework与Jenkins集成发送邮件

6)     构建后操作
Robot results:

构建后的操作,选择“PublishRobot Framework test results”

*Directory ofRobot output 填一个本地路径,要根据你的output文件放在哪里了,默认可以不填

*Thresholds forbuild result  阀值设置,如80%和100%,这里应该是测试用例执行成功率和通过率的设置

 RobotFramework与Jenkins集成发送邮件

注:如果不知道Directoryof Robot output填什么路径,可以通过控制台输出进行定位,如下:

  RobotFramework与Jenkins集成发送邮件

 RobotFramework与Jenkins集成发送邮件

其实report.html和log.html的输出路径是在启动脚本控制的,如下:

 RobotFramework与Jenkins集成发送邮件

Email 信息:

点击“增加构建后操作步骤”,然后点击“Editable Email Notification”,进入邮件内容详细配置界面。

 RobotFramework与Jenkins集成发送邮件

Default Subject: 邮件主题,可以书写成:XXX项目自动化测试通知:$PROJECT_NAME- Build # $BUILD_NUMBER - $BUILD_STATUS! 分析下这几个参数什么意思:$PROJECT_NAME 构建项目的名称,也就是selenium_2_combat;# $BUILD_NUMBER 构建的号码;$BUILD_STATUS构建状态,这几个参数,它会自动读取,按照这种格式书写即可。

Default Content:邮件内容,这块是重点,最能体现报告的重点,我们需要输入以下内容:

<hr/>

(本邮件是程序自动下发的,请勿回复!)<br/><hr/>

项目名称:$PROJECT_NAME<br/><hr/>

构建编号:$BUILD_NUMBER<br/><hr/>

构建状态:$BUILD_STATUS<br/><hr/>

触发原因:${CAUSE}<br/><hr/>

测试报告:<ahref="http://192.168.1.106:8080/job/$PROJECT_NAME/ws/autotest/result/test-report/power-emailable-report.html">http://192.168.1.106:8080/job/autotest/ws/autotest/result/test-report/power-emailable-report.html</a><br/><hr/>

构建日志地址:<ahref="${BUILD_URL}console">${BUILD_URL}console/</a><br/><hr/>

构建地址:<ahref="$BUILD_URL">$BUILD_URL</a><br/><hr/>

构建报告:<ahref="${BUILD_URL}testReport">${BUILD_URL}testReport/</a><br/><hr/>

变更集:${JELLY_SCRIPT,template="html"}<br/><hr/>

其中的红色字体需要修改成自己电脑的IP地址,这样别人才能访问到jenkins上的测试结果。

点击页面上的Advanced settings设置什么时候触发发送邮件的功能:

把默认的trigger给删除掉,然后新增一个trigger,然后选择Always选项,如此便不管构建成功还是失败都会发送邮件。

 RobotFramework与Jenkins集成发送邮件

点击应用后保存,项目配置完成!!!

4.   邮件查收
点击“立即构建”

 RobotFramework与Jenkins集成发送邮件

执行之后,结果显示如下:

 RobotFramework与Jenkins集成发送邮件

对应邮箱查收如下

 RobotFramework与Jenkins集成发送邮件

E.     Jenkins常错误
1.   反向代理设置错误

RobotFramework与Jenkins集成发送邮件


其实就是,系统管理-->系统设置里的Jenkins URL没有写对,原因是配置文件/etc/sysconfig/jenkins我修改了jenkins启动端口,这里没有相应修改

Jenkins URL

 RobotFramework与Jenkins集成发送邮件

将localhost修改为真实地址即可

 RobotFramework与Jenkins集成发送邮件

修改后已不再提示代理问题

 RobotFramework与Jenkins集成发送邮件

2.   点击‘立即获取’插件,报unable to find valid certification path to requested target错误
原因是缺少证书

 RobotFramework与Jenkins集成发送邮件

3.   缺少依赖插件
根据提示到http://updates.jenkins-ci.org/download/plugins/下载对应的插件

 RobotFramework与Jenkins集成发送邮件

4.   校验邮箱的联通性

RobotFramework与Jenkins集成发送邮件


 

如果Test e-mail recipient没有填邮箱信息,则会报如下错误

 RobotFramework与Jenkins集成发送邮件

5.   jenkins邮件配置完后发送测试邮件是成功的,并且也能收到的,构建结束成功后log提示成功,但是没有收到邮件

RobotFramework与Jenkins集成发送邮件


原因是Extended E-mailNotification 的SMTP服务器配置没有使用Jenkins自身的需要专门配置。在系统配置里面多配置一次就行

 RobotFramework与Jenkins集成发送邮件

6.   jenkins调用robot_results.groovy 未生效
配置如下:

任务配置:Default Content引用$DEFAULT_CONTENT变量

 RobotFramework与Jenkins集成发送邮件

$DEFAULT_CONTENT变量配置:${SCRIPT,template="robot_results.groovy"}

 RobotFramework与Jenkins集成发送邮件

robot_results.groovy文件是在$Jenkins_Home/email-templates目录下的

 RobotFramework与Jenkins集成发送邮件

robot_results.groovy内容请看如下:

robot_results.groovy文件下载地址:https://download.csdn.net/download/glongljl/10396246

模板一:

<%  
 import java.text.DateFormat  
 import java.text.SimpleDateFormat  
%> 
<!-- Robot Framework Results --> 
<!DOCTYPE html>
<html>
<style type="text/css">
table {720px;table-layout:fixed;}
td {180px;}
td.title {
    background-color:#343A40;
    text-align: center;
}
td.suite{background-color:#EEE8AA;}
td.case{background-color:#dff0d8;}
td.head{background-color:#1E90FF;}
td.error {background-color:#FF6666;}
table thead tboday tr td {cellspacing:0px;border:1px;}
h2.span{color:white;}
span.pass{color:#66CC00;}
span.fail{color:#FF3333;}
</style>
<body>
<%  
 def robotResults = false  
 def actions = build.actions // List<hudson.model.Action>  
 actions.each() { action ->  
    if( action.class.simpleName.equals("RobotBuildAction") ) { // hudson.plugins.robot.RobotBuildAction  
        robotResults = true 
%>
        <div>
            <table cellpadding="4" align="left">
                <thead>
                    <tr>
                        <td class="title" colspan="4"><h2><span>${project.name}</span><span>  自动化测试报告</span></h2></td>
                    </tr>
                    <tr>
                        <td class="case"><b>详细报告</b></td>
                        <td colspan="3" class="case"><a href="${rooturl}${build.url}robot/report/report.html">点击查看报告详情</a></td>
                    </tr>
                    <tr>
                        <td class="head"><b>用例总数</b></td>
                        <td class="head"><b>通过</b></td>
                        <td class="head"><b>不通过</b></td>
                        <td class="head"><b>通过率</b></td>
                    </tr>
                    <tr>
                        <td class="case"><%= action.result.overallTotal %></td>
                        <td class="case"><b><span class="pass"><%= action.result.overallPassed %></span></b></td>
                        <td class="case"><b><span class="fail"><%= action.result.overallFailed %></span></b></td>
                        <td class="case"><%= action.overallPassPercentage %>%</td>
                    </tr>
                    <tr>
                        <td colspan="2" class="head"><b>Test Name</b></td> 
                        <td class="head"><b>Status</b></td>
                        <td class="head"><b>Elapsed Time</b></td> 
                    </tr>
                </thead>
                <tboday>
<% 
 def suites = action.result.allSuites  
 suites.each() { suite ->   
    def currSuite = suite  
    def suiteName = currSuite.displayName  
    //忽略最上层结构两个占位的元素  
    while (currSuite.parent != null && currSuite.parent.parent != null) {  
        currSuite = currSuite.parent  
        suiteName = currSuite.displayName + "." + suiteName  
    }
%> 
                    <tr>
                        <td colspan="4" class="suite"><b><%= suiteName %></b></td>
                    </tr>
<%  
    DateFormat format = new SimpleDateFormat("yyyyMMdd HH:mm:ss")
    def execDateTcPairs = []
    suite.caseResults.each() { tc ->  
        Date execDate = format.parse(tc.starttime)
        execDateTcPairs << [execDate, tc]
    }
    //按执行日期、显示名称进行排序
    execDateTcPairs = execDateTcPairs.sort{ a,b -> a[1].displayName <=> b[1].displayName }
    execDateTcPairs = execDateTcPairs.sort{ a,b -> a[0] <=> b[0] }
    execDateTcPairs.each() {
        def execDate = it[0]
        def tc = it[1]  
%>
                    <tr>  
                        <td colspan="2" class="case"><%= tc.displayName %></td>  
                        <td class="case"><b><span style="color:<%= tc.isPassed() ? "#66CC00" : "#FF3333" %>"><%= tc.isPassed() ? "PASS" : "FAIL" %></span></b></td>  
                        <td class="case"><%= tc.getDuration().intdiv(60000)+""+(tc.getDuration()-tc.getDuration().intdiv(60000)*60000).intdiv(1000)+"" %></td>  
                    </tr>  
        
<%
        if(tc.errorMsg != null) {
%>
                    <tr>
                        <td class="error"><b><span>错误描述:</span></b></td>
                        <td class="error" colspan="3"><span><%= tc.errorMsg%></span></td>
                    </tr>
<%                }%>
<%  
            } // tests  
        } // suites 
%>  
                </tboday>
            </table>
        </div>
<%  
    } // robot results  
}  
    if (!robotResults){ 
%> 
    <p>No Robot Framework test results found.</p>  
<%}%>
</body>
</html>
robot测试报告模板一

模板二:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<style type="text/css">
/*base css*/
a{color:#4a72af}
body{background-color:#e4e4e4}
body,p{margin:0;padding:0}
img{display:block}
h1,h2,h3,h4,h5,h6{margin:0 0 .8em 0}
h3{font-size:28px;color:#444!important;font-family:Arial,Helvetica,sans-serif}
h4{font-size:22px;color:#4a72af!important;font-family:Arial,Helvetica,sans-serif}
h5{font-size:18px;color:#444!important;font-family:Arial,Helvetica,sans-serif}
p{font-size:12px;color:#444!important;font-family:"Lucida Grande","Lucida Sans","Lucida Sans Unicode",sans-serif;line-height:1.5}

table.robotstat {
  border: 1px solid black;
  border-collapse: collapse;
  empty-cells: show;
  margin: 0px 1px;
  table-layout: fixed;
  word-wrap: break-word;
  font-size: 1em;
  border-1px;
}

tr.test_column_robot {
  background-color:#C6C6C6;
}

ol li img{display:inline;height:20px}
/*div styles*/
.news{text-align:center;padding-top:15px;}
.content{720px;margin:0 auto;background-color:white}
.round_border{margin-bottom:5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;margin-top:0;font-size:14px;padding:6px;border:1px solid #ccc}
.status{background-color:<%=
            build.result.toString() == "SUCCESS" ? 'green' : 'red' %>;font-size:28px;font-weight:bold;color:white;720px;height:52px;margin-bottom:18px;text-align:center;vertical-align:middle;border-collapse:collapse;background-repeat:no-repeat}
.status .info{color:white!important;text-shadow:0 -1px 0 rgba(0,0,0,0.3);font-size:32px;line-height:36px;padding:8px 0}
.main img{38px;margin-right:16px;height:38px}
.main table{font-size:14px;}
.main table th{text-align:right;}
.bottom-message{720px;cellpadding:5px;cellspacing:0px}
.bottom-message .message{font-size:13px;color:#aaa;line-height:18px;text-align:center}
.bottom-message .designed{font-size:13px;color:#aaa;line-height:18px;font-style: italic;text-align:right}
img.cartoon { 36px; display:inline}
</style>
<body>
<div class="content round_border">
        <div class="status">
            <p class="info">构建状态 <%= build.result.toString().toLowerCase() %></p>
        </div>
        <!-- status -->
        <div class="main round_border">
            <table>
                <tbody>
                    <tr>
                        <th>项目名称:</th>
                        <td>${project.name}</td>
                    </tr>
                    <tr>
                        <th>构建轮次:</th>
                        <td><a
                            href="${rooturl}${build.url}">${build.displayName}(点击查看此轮构建信息)</a></td>
                    </tr>
                    <tr>
                        <th>构建时间:</th>
                        <td>${it.timestampString}</td>
                    </tr>
                    <tr>
                        <th>构建时长:</th>
                        <td>${build.durationString}</td>
                    </tr>
          <tr>
            <th>构建缘由:</th>
            <td><% build.causes.each() { cause -> %> ${cause.shortDescription} <% } %></td>
          </tr>
          <tr>
            <th>测试报告:</th>
            <td><a
              href="${rooturl}${build.url}robot">点击查看测试报告详情</a></td>
          </tr>
          <tr>
            <!-- test stat -->
            <th>测试统计:</th></br>
            <td>
            <table id="robotstat" class="robotstat">
            <thead>
            <tr id="test_column_robot" class="test_column_robot">
            <th>测试总用例数</th>
            <th>失败用例数</th>
            <th>测试通过率</th>
            </tr>
            </thead>
            <tbody>
            <tr>
            <%  def robotTestResultAction = it.getAction("hudson.plugins.robot.RobotBuildAction") %>
            <td>${robotTestResultAction.getTotalCount()}</td>
            <td>${robotTestResultAction.getFailCount()}</td>
            <td>${robotTestResultAction.getOverallPassPercentage()}%</td>
            </tr>
            </tbody>
            </table>
            </td>
          </tr>
                    <tr>
                        <th>变更记录:</th>
                        <td><a
                            href="${rooturl}${build.url}changes">点击查看变更记录</a></td>
                    </tr>
                    <tr>
                        <td colspan="2"> </td>
                    </tr>
                </tbody>

            </table>

        </div>
        <!-- main -->
        <% def artifacts = build.artifacts
            if(artifacts != null && artifacts.size() > 0) { %>

        <div class="artifacts round_border">
            <b>Build Artifacts:</b>
            <ul>
            <%      artifacts.each() { f -> %>
                <li><a href="${rooturl}${build.url}artifact/${f}">${f}</a></li>
            <%      } %>
            </ul>
        </div>
        <% } %>
        <!-- artifacts -->

        <% def changeSet = build.changeSet
        if(changeSet != null) {
            def hadChanges = false
            def count = 0 %>

        <div class="details round_border">
            <b>变更详细:</b>
            <ol>
            <%  changeSet.each() { cs ->
                    hadChanges = true
                    def aUser = cs.author %>
                <li>${cs.msgAnnotated} (${aUser.displayName})
                    (<a href="${rooturl}${build.url}changes#detail${count}">detail</a>)</li>
            <%      count ++
                }  %>
            </ol>
        </div>
        <% } %>
        <!-- details -->
    </div>
    <!-- content -->

    <table class="bottom-message" align="center">
        <tr>
            <td class="message">You are receiving this email because you
                are relavent with this build<br>
            </td>
        </tr>
        <tr>
            <td colspan="2" class="designed">designed by @YTO  </td>
        </tr>
    </table>
    <!-- bottom message -->

</body>

结果邮箱收到时没有读取robot_results.groovy的模板内容

 RobotFramework与Jenkins集成发送邮件

经过分析:

是由于缺少groovy-postbuild插件操作的,安装groovy-postbuild插件

 RobotFramework与Jenkins集成发送邮件

groovy-postbuild插件安装完之后,重新执行计划,邮箱可以获取到模板信息的,如下:

 RobotFramework与Jenkins集成发送邮件

7.   Robot Framework - Jenkins 的测试报告打不开
点击邮件的链接

 RobotFramework与Jenkins集成发送邮件

以及点击Jenkins上的链接

 RobotFramework与Jenkins集成发送邮件

结果界面都会报如下错误:

 RobotFramework与Jenkins集成发送邮件

解决办法:

1. 如果你是用命令行开启的

关闭Jenkins,修改开启命令如下,重新开启:

java -Dhudson.model.DirectoryBrowserSupport.CSP= -jar E:Jenkinsjenkins.war   

2. 如果你是用msi安装的

找到jenkins.xml 文件,修改如下

<arguments>-Xrs-Xmx256m -Dhudson.model.DirectoryBrowserSupport.CSP=-Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar"%BASE%jenkins.war" --httpPort=8080</arguments>   

3. 如果你是用tomcat 启动的

有个临时的解决方法

打开jenkins 首页——>进入系统管理——>进入脚本命令行

在输入框输入如下代码,并执行

System.setProperty("hudson.model.DirectoryBrowserSupport.CSP","")

 RobotFramework与Jenkins集成发送邮件

注:方法3 都可以使用,但是重启后就失效了,需要重新执行

4. 访问端解决办法

以Firefox为例:

到 about:config 设置

security.csp.enable= false

之后问题正常显示如下:

RobotFramework与Jenkins集成发送邮件


---------------------
作者:glong168
来源:CSDN
原文:https://blog.csdn.net/glongljl/article/details/80212611
版权声明:本文为博主原创文章,转载请附上博文链接!