一、背景

RF的结果报告可以方便我们查看每一条用例集、用例的执行结果统计,但是有的项目涉及到一些数据的比对,希望能够直观到看到数据,原生的测试报告就无法满足这个需求了。

  • 原生的报告

  • 项目需求报告格式

二、解决方案

2.1 流程图

  • 解析output.xml,将用例的相关信息和执行结果获取
  • 通过API和数据库获取需要展示的数据
  • 将上述两步的数据封装到一个数据列表中,方便后面进行模板渲染
  • 根据需求画HTML的报告模板
  • 对模板进行数据填充渲染,生成报告文件
  • 邮件发送报告
2.2 output.xml解析

测试用例相关的信息和运行结果,我们可以通过解析RF的output.xml文件来进行获取

2.2.1获取用例运行情况的统计

import xml.dom.minidom
import xml.etree.ElementTree # 打开xml文档
dom = xml.dom.minidom.parse('E:\\robot\\fightdata_yuce\\results\\output.xml')
root2 = xml.etree.ElementTree.parse('E:\\robot\\fightdata_yuce\\results\\output.xml') # 得到文档元素对象
root = dom.documentElement
total = root.getElementsByTagName('total');
total_len = len(total)
# total的stat节点个数
total2 = root2.getiterator("total")
total_stat_num = len(total2[total_len-1].getchildren())
statlist = root.getElementsByTagName('stat'); def get_total_statistics():
list = []
for i in range(0,total_stat_num):
d = dict()
d['fail'] = int(statlist[i].getAttribute("fail"))#失败用例数
d['pass'] = int(statlist[i].getAttribute("pass"))#成功用例数
d['total'] = d['fail']+d['pass']#用例总数
d['percent'] = ('{:.2%}'.format(d['pass'] / d['total']))#用例百分比
list.append(d)
return list
2.2.2 获取用例信息
  • 用例的组织结构

  • 获取用例信息和执行结果

    用例结构是多个suite,每个suite下有4条case

import xml.dom.minidom
import xml.etree.ElementTree # 打开xml文档
dom = xml.dom.minidom.parse('E:\\robot\\xxx\\results\\output.xml')
root2 = xml.etree.ElementTree.parse('E:\\robot\\xxx\\results\\output.xml')
tree3=root2.getroot()
# 获取suite下的子节点
def getcase():
casedict = {}
testlist2 = []
for elem in tree3.iterfind('suite/suite'):
a = elem.attrib
suitedict = {}
testlist2.append(suitedict) #每一个用例集合存入列表
testlist = []
suitename = a['name']#获取用例结合的名字
for test in elem.iter(tag='test'):
b=test.attrib
for data in test.iterfind('status'):
casename = b['name'] #获取用例的名字
c=data.attrib
status=c['status'] #获取每条用例的执行结果
casedict['casename'] = casename #用例名字存入字典
casedict['status'] = status #用例执行结果存入字典
testlist.append(casedict) #每一条用例的名字和执行结果作为字典存入列表
casedict = {}
suitedict['suitename']=suitename
suitedict['test']=testlist
return testlist2 #最终返回的就是[{'suitename': 'xxx', 'test': [{'casename': '01 xxx', 'status': 'PASS'},
#{'casename': '02 xxx', 'status': 'PASS'}
2.3 数据填充

通过前面获取的获取,填充到jinja2的模板中,会生成另外一个有数据的html文件

from jinja2 import Environment, FileSystemLoader
import parsexml def generate_html(data):
env = Environment(loader=FileSystemLoader('./')) # 加载模板
template = env.get_template('report.html')
# template.stream(body).dump('result.html', 'utf-8')
data=parsexml.get_total_statistics()#获取解析的xml的用例统计数据
data2=parsexml.getcase()#获取测试用例信息和执行结果
with open("result.html", 'w',encoding='utf-8') as fout:
html_content = template.render(data=data,data2=data2)
fout.write(html_content) # 写入模板 生成html
2.4 jinja2模板介绍

jinja2模板的原理就是,通过先创建一个html的模板文件,然后将数据渲染到模板文件,生成一个渲染后的html文件,该文件会显示填充的数据

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>战娃利润中心指标自动化测试报告</title>
</head>
<body>
<div style="width:100%;float:left">
<table cellspacing="0" cellpadding="4" border="1" align="left">
<thead>
<tr bgcolor="#F3F3F3">
<td style="text-align:center" colspan="9"><b>战娃利润中心日报自动检测</b></td>
</tr>
<!-- <tr>
<td bgcolor="#F3F3F3" style="width:80px"><b>详细报告:</b></td>
<td colspan="8">
<a href="${rooturl}${build.url}robot/report/report.html">点击查看报告详情</a>
</td>
</tr>-->
<tr>
<td bgcolor="#F3F3F3" style="width:100px"><b>指标预测规则:</b></td>
<td colspan="8">
<a href="https://docs.qq.com/sheet/DT1ZBZHdGdXJkVWFL">点击查看指标预测规则</a>
</td>
</tr>
<!--这里定义的是用例的统计信息-->
<tr bgcolor="#F3F3F3">
<td><b>用例总数</b></td>
<td><b>通过</b></td>
<td style="width:60px"><b>不通过</b></td>
<td colspan="6"><b>通过率</b></td>
</tr>
<!--通过2.3的渲染获取的数据,在这里进行取值填充-->
<tr>
<td>{{data['total']}}</td>
<td><b><span style="color:#66CC00">{{data['pass']}}</span></b></td>
<td><b><span style="color:#FF3333">{{data['fail']}}</span></b></td>
<td colspan="6">{{data['percent']}}</td>
</tr>
<!-- 这里定义的是用例的表头信息-->
<tr bgcolor="#F3F3F3">
<td colspan="2"><b>Test Name</b></td>
<td><b>执行结果</b></td>
<td><b>预测上限</b></td>
<td><b>预测下限</b></td>
<td><b>预测值</b></td>
<td><b>实际值</b></td>
<td><b>差值(预测-实际)</b></td>
<td><b>差值百分比</b></td>
</tr>
</thead>
<tbody>
<!--通过2.3的渲染获取的数据data2,也就是用例信息数据,在这里进行取值填充,由于涉及多个suite,
每个suite下有多条case,这里通过两层循环取每条case的相关信息 -->
{% for data in data2 %}
<tr>
<td colspan="9"><b>{{data['suitename']}}</b></td>
</tr> <tr>
{% for c2 in data['test'] %}
<td colspan="2">{{c2['casename']}}</td>
{% if c2['status']=='PASS' %}
<td><b><span style="color:#66CC00">{{c2['status']}}</span></b></td>
{% else %}
<td><b><span style="color:#FF3333" >{{c2['status']}}</span></b></td>
{% endif %}
<td>{{c2['max']}}</td>
<td>{{c2['min']}}</td>
{% if c2['casename']=='01 GMV' %}
<td>{{c2['yhat']}}</td>
{% else %}
<td>--</td>
{% endif %}
<td>{{c2['real']}}</td>
{% if c2['casename']=='01 GMV' %}
<td>{{c2['reduce']}}</td>
{% else %}
<td>--</td>
{% endif %}
{% if c2['casename']=='01 GMV' %}
<td>{{c2['percent']}}</td>
{% else %}
<td>--</td>
{% endif %}
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
</body>
</html>
2.5 发送邮件

将上述渲染生成的有数据的html文件作为测试报告进行邮件发送

# !/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib, time, os
from email.mime.text import MIMEText
from email.header import Header
import generate def send_mail_html(file):
sender = 'ccc@fulu.com' #发件人
mail_to =['aa@fulu.com','bb@fulu.com] #收件人
t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) #获取当前时间
subject = '战娃利润中心指标自动化测试报告' + t #邮件主题
smtpserver = 'smtp.qiye.aliyun.com' #发送服务器地址
username = 'cc@fulu.com' #用户名
password = '123456' #密码
f = open(file, 'rb')
mail_body = f.read()
f.close() msg = MIMEText(mail_body, _subtype='html', _charset='utf-8')
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] = sender
msg['To'] = ";".join(mail_to) try:
smtp = smtplib.SMTP()
smtp.connect(smtpserver)
smtp.login(username, password)
smtp.sendmail(sender, mail_to, msg.as_string())
except:
print("邮件发送失败!")
else:
print("邮件发送成功!")
finally:
smtp.quit() def result():
file = 'result.html' #渲染后的html报告文件
result = {}
generate.generate_html(result)
send_mail_html(file)

邮件展示结果

三、回顾整个实现过程

在当初接到该需求的时候,尝试在网上找相关的实现方案,其中jenkins自带的RF的插件可以实现报告的解析和邮件发送,第一版我们就是采用该报告模板进行推送

第一版报告,参考该博文https://blog.csdn.net/qq_38317509/article/details/81316940

在需要展示每条用例的具体数据信息的时候,也是尝试对该模板文件进行修改取值,并查看了插件的源码实现,发现无法进行这种个性化的数据的取值,源码只是返回了用例信息、执行状态和执行时间,以及失败时候的msg,那能不能通过msg这个关键字来做文章呢,也就是把需要查看的数据通过msg打印显示出来,然而发现只有case失败的时候才显示用例,成功的时候不显示,而且数据以log显示,查看不是那么清晰

基于上述情况,发现了jinja2这个包,于是就放弃了jenkins的插件,通过自定义报告模板,然后填充数据的方式,这样灵活度就大大提高了,后续的个性化需求也方便去定制开发了,目前我们还是依赖jenkins进行测试任务的触发,我们希望将RF这块也容器化,这块你们有相关的实践吗,欢迎指导~

福禄ICH·质量保证部
福小龙

jinja2快速实现自定义的robotframework的测试报告的更多相关文章

  1. Robot Framework自动化测试框架核心指南-如何使用Java编写自定义的RobotFramework Lib

    如何使用Java编写自定义的RobotFramework Lib 本文包括2个章节 1. Robot Frdamwork中如何调用java Lib库 2.使用 java编写自定义的Lib 本文作者为: ...

  2. RobotFramework自动化测试框架-使用Python编写自定义的RobotFramework Lib

    使用Python构建Lib工程 可以用来开发Python Lib的IDE工具有很多,常见的有Pycharm,Eclipse with PyDev插件等,而且在RobotFramework官网中也已经提 ...

  3. 常用输入法快速输入自定义格式的时间和日期(搜狗/QQ/微软拼音)

    几个主流的输入法输入 rq 或者 sj 都可以得到预定义格式的日期或者时间.然而他们都是预定义的格式:当我们需要一些其他格式的时候该怎么做呢? 本文将介绍几个常用输入法自定义时间和日期格式的方法. 主 ...

  4. Jinja2模版语言自定义filter的使用

    Jinja2模版语言,自带有一些filter,能够在前端的模版中控制数据按照相应的方式显示.比如以下两种filter,分别能在前端控制数字的近似精度显示和根据字符串长度补齐: round(value, ...

  5. django -使用jinja2模板引擎 自定义的过滤器

    setting.py中 TEMPLATES = [ { 'BACKEND': 'django.template.backends.jinja2.Jinja2', 'DIRS': [os.path.jo ...

  6. intellij idea 设置Live Template快速生成自定义代码块

    一.设置 类似于宏,话不多少,上步骤 File----->Setting 选择Live Template 新建触发规则 新建触发key 输入模版text 选择在哪个环境触发 选java 如果是其 ...

  7. Flask的jinja2模板中自定义过滤器的使用

    大部分的模板引擎都是支持过滤器功能的,jinja2也不例外,它提供了丰富的内置过滤器,但是有些时候还是没办法满足我们的需求,好在jinja2支持自定义过滤器,下面是一个简单的例子. 下面的例子完成了自 ...

  8. Ansible_使用jinja2模板部署自定义文件

    一.jinja2简介 1.jinja2模板 1️⃣:Ansible将jinja2模板系统用于模板文件,Ansible还使用jinja2语法来引用playbook中的变量 2️⃣:变量和逻辑表达式置于标 ...

  9. Android中快速实现自定义字体!

    前言:我们都知道,Android中默认的字体是黑体,而大多数app也都是使用的这种字体,但我们发现,大多数app中,个别地方字体非常好看,例如app的标题栏,菜单栏等地方,那他们是怎么做到的呢?有两种 ...

随机推荐

  1. 009_go语言中的slices分片

    代码演示 package main import "fmt" func main() { s := make([]string, 3) fmt.Println("emp: ...

  2. 006_go语言中的if else条件语句

    代码演示 package main import "fmt" func main() { if 7%2 == 0 { fmt.Println("7 is even&quo ...

  3. javaweb 测试

    题目要求: 1登录账号:要求由6到12位字母.数字.下划线组成,只有字母可以开头:(1分) 2登录密码:要求显示“• ”或“*”表示输入位数,密码要求八位以上字母.数字组成.(1分) 3性别:要求用单 ...

  4. docker安装gitlab并部署CICD

    摘要 本文主要实现了在docker下安装gitlab,将gitlab绑定在宿主机的180端口,将gitlab的clone的URL添加指定端口号:部署了CI/CD,并公布了测试项目. 安装docker[ ...

  5. Java基础—继承

    继承是面向对象的核心特征之一,是由已有类创建新类的机制.利用继承机制,可以先创建一个具有共性的一般类,然后根据该一般类创建具有特殊性的新类,新类继承一般类的属性和方法,并根据需要增加自己的新属性和方法 ...

  6. JS 与 jQery 的区别主要在于 DOM

    //目前正在学习前端阶段,把知识点整理.保存下来以便日后查看 首先引入jQery: 需要先引入css,再引入js: jQery需要在js前引入,再引入框架,最后才是js的引入:css也相同,先引入框架 ...

  7. C#LeetCode刷题之#507-完美数(Perfect Number)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3879 访问. 对于一个 正整数,如果它和除了它自身以外的所有正因 ...

  8. matlab使用libsvm入门教程——使用matlab安装配置libsvm以及一个svm分类实例

    前言 此教程专注于刚入门的小白, 且博客拥有时效性, 发布于2019年3月份, 可能后面的读者会发现一些问题, 欢迎底下评论出现的问题,我将尽可能更新解决方案. 我开始也在如何安装libsvm上出现了 ...

  9. 国人开源了一款小而全的 Java 工具类库,厉害啊!!

    最近栈长看到了一款小而全的 Java 工具类库:Hutool,Github 已经接近 14K Star 了,想必一定很优秀,现在推荐给大家,很多轮子不要再造了! Hutool 是什么 Hutool 是 ...

  10. 聊一聊mycat数据库集群系列之双主双重实现

    最近在梳理数据库集群的相关操作,现在花点时间整理一下关于mysql数据库集群的操作总结,恰好你又在看这一块,供一份参考.本次系列终结大概包括以下内容:多数据库安装.mycat部署安装.数据库之读写分离 ...