背景:
公司使用jenkins进行环境部署、自动化定时执行完成持续集成流水线,但并不希望每天点开jenkins job去查看执行结果,这样很麻烦,希望对每日集成部署环境得自动化测试结果进行汇总,邮件接收每日整体结果即可,有异常时才会进行详细报告查看。所以我们需要对每日持续集成环境中得自动化结果收集并整合成一份整体报告,并以邮件得形式发送给相关人员。
一、通过jenkinsServer获取最新job日志
本次实现用到得语言和技术为: java、 JenkinsServer。 通过JenkinsServer可以获取指定job最后一次构建日志:
a, 首先是连接jenkins,这里使用token, 可绕开403 forbidden的问题
/**
* 连接 Jenkins
*/
public static JenkinsServer connection() {
JenkinsServer jenkinsServer = null;
try {
jenkinsServer = new JenkinsServer(new URI(jenkinsUrl), username,token); // 使用token, 可绕开403 forbidden的问题
} catch (URISyntaxException e) {
e.printStackTrace();
}
return jenkinsServer;
}
备注: jenkins用户得api token需要在jenkins中创建
b, 获取指定job得最新构建日志:
/**
* 获取最后一次构建的日志
* @param jenkinsServer
* @param jobName
* @return
* @throws Exception
*/
public static String getConsoleOutLog(JenkinsServer jenkinsServer, String jobName) throws Exception{
JobWithDetails jobWithDetails = jenkinsServer.getJob(jobName).details();
ConsoleLog consoleLog = jobWithDetails.getLastBuild().details().getConsoleOutputText(0); //获取最后一次构建的日志
String consoleTxt = consoleLog.getConsoleLog();
return consoleTxt;
}
二、解析日志
需要根据日志内容匹配执行结果,主要是用到正则匹配:
/**
* 解析日志获取ui自动化执行结果
* @param log
* @return
*/
public String[] analysisUILogResult(String log) {
String reg = "(Total tests run: )(\\d*, )(Failures: )(\\d*, )(Skips: )(\\d*)";
Pattern p = Pattern.compile(reg);
Matcher m = p.matcher(log);
String logResult = "";
while(m.find()){
logResult = m.group();
System.out.println(logResult);
}
String[] temp = logResult.split(",");
for (int i = 0; i < temp.length; i++) {
temp[i] = temp[i].split(":")[1].replace(" ", "");
}
String[] result = new String[5];
if (temp.length !=0) {
result[1] = temp[0]; //总数
result[3] = temp[1]; //失败
result[4] = temp[2]; //跳过
result[2] = String.valueOf(Integer.valueOf(temp[0]) - Integer.valueOf(temp[1]) - Integer.valueOf(temp[2])); //成功
result[0] = String.valueOf((double)(Math.round(Integer.valueOf(result[2])*10000/Integer.valueOf(result[1]))/10000.0)*100) + "%"; //通过率
}
return result;
}
三、整合多个job得结果:
通常会执行接口自动化和ui自动化,需要整合两个job得结果:
/**
* 获取整合后的执行结果信息
* @param jobNames
* @return
*/
public HashMap<String, String[]> getConsolidateReport(String[] jobNames) {
JenkinsServer jenkinsServer = connection();
String log = "";
String[] resultTemp = new String[5];
HashMap<String, String[]> allResultMap = new HashMap<>();
if (jobNames.length == 0) {
jobNames = new String[]{"接口/AutoTest-YC-接口", "AutoTest-Selenium_AutoTest"};
}
try {
for (int i = 0; i < jobNames.length; i++) {
String[] resultMap = new String[6]; //多一个构建链接
log = getConsoleOutLog(jenkinsServer, jobNames[i]);
if (jobNames[i].contains("Selenium")) {
System.out.println("UI自动化结果为(job名为"+ jobNames[i] +"):\n");
resultTemp = analysisUILogResult(log);
System.out.println("【成功率】:" + resultTemp[0] + "\n"+
"【用例总数】:" + resultTemp[1] + "\n"+
"【成功】:" + resultTemp[2] + "\n"+
"【失败】:" + resultTemp[3] + "\n"+
"【跳过】:" + resultTemp[4] + "\n");
resultMap[5] = getLastBuildLink(jobNames[i]);
}else if(jobNames[i].contains("-YC-")) {
System.out.println("\n云测pub接口自动化结果为(job名为"+ jobNames[i] +"):\n");
resultTemp = analysisYCLogResult(log);
System.out.println("【成功率】:" + resultTemp[0] + "\n"+
"【用例总数】:" + resultTemp[1] + "\n"+
"【成功】:" + resultTemp[2] + "\n"+
"【失败】:" + resultTemp[3] + "\n"+
"【跳过】:" + resultTemp[4] + "\n");
resultMap[5] = "https://yc.mingyuanyun.com/#/125/org_detail";
}
for (int j = 0; j < resultTemp.length; j++) {
resultMap[j] = resultTemp[j];
}
allResultMap.put(jobNames[i], resultMap);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return allResultMap;
}
四、编辑邮件模板并发送邮件
a, 模板:
<body style="color: #666; font-size: 14px; font-family: 'Open Sans',Helvetica,Arial,sans-serif;">
<div class="box-content" style="width: 80%; margin: 20px auto; max-width: 800px; min-width: 600px;">
<div class="header-tip" style="font-size: 12px;
color: #aaa;
text-align: right;
padding-right: 25px;
padding-bottom: 10px;">
自动化测试整合报告
</div>
<div class="info-top" style="padding: 15px 25px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
background: {0};
color: #fff;
overflow: hidden;
line-height: 32px;">
<img src="cid:icon-alarm" style="float: left; margin: 0 10px 0 0; width: 32px;" /><div style="color:#010e07"><strong>自动化测试整合报告(UI+pub接口)</strong></div>
</div>
<div class="info-wrap" style="border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
border:1px solid #ddd;
overflow: hidden;
padding: 15px 15px 20px;">
<div class="tips" style="padding:15px;">
<p style=" list-style: 160%; margin: 10px 0;">Hi All,</p>
<p style=" list-style: 160%; margin: 10px 0;">{1}</p>
</div>
<table class="list" style="width: 100%; border-collapse: collapse; border-top:1px solid #eee; font-size:12px;">
<thead>
<tr style=" background: #fafafa; color: #333; border-bottom: 1px solid #eee;">
{3}
</tr>
</thead>
<tbody>
{4}
</tbody>
</table>
<br />
<div class="time" style="text-align: right; color: #999; padding: 0 15px 15px;">{2}</div>
<br>
</div>
</div>
</body>
b, 设置邮件内容:
/**
* 设置邮件内容
* @return
* @throws IOException
*/
private String buildContent(String[] jobNames) throws IOException {
//加载邮件html模板
String path = System.getProperty("user.dir") + "/conf/pod-scale-alarm.html";
InputStream inputStream = new FileInputStream(path);;
BufferedReader fileReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer buffer = new StringBuffer();
String line = "";
try {
while ((line = fileReader.readLine()) != null) {
buffer.append(line);
}
} catch (Exception e) {
// LOGGER.error("读取文件失败,fileName:{}", fileName, e);
} finally {
inputStream.close();
fileReader.close();
}
String contentText = "以下自动化测试整体执行结果(详情点击任务名称查看):<br>";
//邮件表格header
String header = "<td>自动化任务名称</td><td>成功率</td><td>用例总数</td><td>成功</td><td>失败</td><td>跳过</td>";
StringBuilder linesBuffer = addResult(jobNames);
//蓝色
String emailHeadColor = "#9fc5e8";
String date = DateFormatUtils.format(new Date(), "yyyy/MM/dd HH:mm:ss");
//填充html模板中的五个参数
String htmlText = MessageFormat.format(buffer.toString(), emailHeadColor, contentText, date, header, linesBuffer.toString());
//改变表格样式
htmlText = htmlText.replaceAll("<td>", "<td style=\"padding:6px 10px; line-height: 150%;\">");
htmlText = htmlText.replaceAll("<tr>", "<tr style=\"border-bottom: 1px solid #eee; color:#666;\">");
System.out.println(htmlText);
return htmlText;
}
/**
* 将jenkins报告结果拼接到html中
* @param jobNames
* @return
*/
public StringBuilder addResult(String[] jobNames) {
String jobName = "",key="", value="";
String[] valueList = new String[5];
//获取jenkins整合结果信息
ConsolidateJenkinsReport jenkins = new ConsolidateJenkinsReport("http://10.5.10.154:8085/", "admin", "116a2d55eb2dd1768aec59defe3a8cf4c6");
HashMap<String, String[]> resultContent = jenkins.getConsolidateReport(jobNames);
// 将jenkins报告结果拼接到html中
StringBuilder linesBuffer = new StringBuilder();
for (Entry<String, String[]> entry : resultContent.entrySet()) {
jobName = entry.getKey();
valueList = entry.getValue();
linesBuffer.append("<tr><td><a href=" + valueList[5] + " target=\"_blank\">" + jobName + "</a></td>"); //把job名称加入,并添加链接
for (int i = 0; i < valueList.length-2; i++) { //把总体结果信息加入
linesBuffer.append("<td>" + valueList[i] + "</td>");
}
linesBuffer.append("<td>" + valueList[4] + "</td></tr>"); //最后一个属性格式单独处理
}
return linesBuffer;
}
c, 发送邮件:
/**
* 发送html格式的邮件。普通常规方法
* @param receiverList
* @throws MessagingException
* @throws IOException
*/
public void sendEmail(String[] jobNames, InternetAddress[] receiverList, String productVersion) throws MessagingException, IOException {
System.err.println(jobNames[0]);
Properties props = new Properties();
props.setProperty("mail.smtp.host", "smtp.exmail.qq.com");
props.setProperty("mail.transport.protocol", "smtp");
props.setProperty("mail.smtp.auth", "true");
props.setProperty("mail.smtp.connectiontimeout", "20000");
props.setProperty("mail.smtp.timeout", "20000");
// 得到回话对象
Session session = Session.getInstance(props);
// 获取邮件对象
Message message = new MimeMessage(session);
// 设置发件人邮箱地址
message.setFrom(new InternetAddress("chengk02@mingyuanyun.com"));
// 设置收件人邮箱地址
message.setRecipients(Message.RecipientType.TO, receiverList);
message.setRecipients(Message.RecipientType.CC, receiverList);
// message.setRecipient(Message.RecipientType.TO, new InternetAddress("chengk02@mingyuanyun.com"));//一个收件人
// 设置邮件标题
message.setSubject("自动化测试结果报告:" + productVersion);
//邮箱内容以及附件
Multipart multipart=new MimeMultipart();
try {
multipart.addBodyPart(createContent(buildContent(jobNames)));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
message.setContent(multipart);
// 得到邮差对象
Transport transport = session.getTransport();
// 连接自己的邮箱账户
transport.connect("xxxxx@xxxx.com", "xxxxxFUBYBxCjw");// 密码为QQ邮箱开通的stmp服务后得到的客户端授权码
// 发送邮件
transport.sendMessage(message, message.getAllRecipients());
transport.close();
}
d, 邮件效果:
点击自动化任务名称可跳转至jenkins中该job页面