python - 通过Robot Framwork结果xml 生产自定义报告
Max.Bai
2017-08-03
1. 准备
1.1 安装pyH
1.2 安装lxml
2. 生成html 文件
根据robot framwork 结果xml文件 按照指定编码格式生成 html格式报告文件
# -*- coding:utf-8 -*-
# @file: generateRFreport.py
# @purpose: a robotframework htm report generator
# @author: Max Bai <wangzi611@163.com>
__doc__ = """The generateRFreport.py is generate html report from robotframework xml report.
"""
__author__ = "Max Bai <wangzi611@163.com>"
__version__ = '$Revision: 1 $'
__date__ = '$Date$'
from xml.etree import ElementTree
from pyh import *
import sys # 1
"""
Generate html format test report from xml result of robot framework.
Args:
input xml file path: the xml result path
output report file path: the output html report file path
encode: html file encodeing e.g: GBK, UTF-8
Sample:
generateRFreport c:\temp\output.xml c:\temp\report.html utf-8 #generate utf-8 encode format html report
generateRFreport c:\temp\output.xml c:\temp\report.html GBK #generate GBK encode format html report
"""
def main(argv):
reload(sys) # 2
sys.setdefaultencoding('utf-8') # 3
print argv[0]
print argv[1]
print argv[2]
print argv[3]
gr = GenerateReport("Automation Test Report")
suiteinfo = gr.get_suite_info(argv[1])
gr.inthtml(suiteinfo[0], argv[3])
total = gr.get_total_data(argv[1])
gr.addSummary(total[0], total[1], suiteinfo[1], suiteinfo[2])
cases = gr.get_cases_data(argv[1])
gr.addDetail(cases)
gr.writeTofile(argv[2], argv[3])
class GenerateReport():
def __init__(self, title):
self.page = PyH(title)
def print_node(self, node):
print "=============================================="
# print "node.attrib:%s" % node.attrib
print "node.tag:%s" % node.tag
# print "node.text:%s" % node.text
if ("name" in node.attrib) > 0:
print "node.attrib['name']:%s" % node.attrib['name']
if ("status" in node.attrib) > 0:
print "node.attrib['status']:%s" % node.attrib['status']
def get_suite_info(self, file):
"""
read xml and return the table
:return:
"""
#root = ElementTree.parse(r"c:/temp/output.xml")
root = ElementTree.parse(file)
#root = ElementTree.fromstring(text)
suite = root.find("./suite")
suteinfo = [0, 0, 0] #name, start, end
suteinfo[0] = suite.attrib['name']
status = suite.find("status")
suteinfo[1] = status.attrib['starttime']
suteinfo[2] = status.attrib['endtime']
return suteinfo
def get_cases_data(self, file):
"""
read xml and return the table
:return:
"""
#root = ElementTree.parse(r"c:/temp/output.xml")
root = ElementTree.parse(file)
#root = ElementTree.fromstring(text)
node_findall = root.findall(".//test")
cases = []
for node in node_findall:
case = [0, 0, 0, 0]
# case name
case[0] = node.attrib['name']
# case result
status = node.find("status")
case[1] = status.attrib['status']
# case failed text
if status.text is None:
case[2] = ''
else:
case[2] = status.text
# case tags
case[3] = ''
tags = node.find("tags")
if tags is None:
case[3] = ''
else:
case[3] = ','.join([tag.text for tag in tags.findall("tag")])
cases.append(case)
return cases
def get_total_data(self, file):
"""
:param file:
:return:
"""
# 加载XML文件(2种方法,一是加载指定字符串,二是加载指定文件)
#root = ElementTree.parse(r"c:/temp/output.xml")
root = ElementTree.parse(file)
#root = ElementTree.fromstring(text)
node_findall = root.findall(".//total/stat")
total = [0, 0]
for node in node_findall:
total[0] = node.attrib['pass']
total[1] = node.attrib['fail']
# total.append(node.attrib['pass'])
# total.append(node.attrib['fail'])
return total
def inthtml(self, h1title, ec='utf-8'):
"""
inital html head and css
:return:
"""
css="<style type='text/css'>h1{\ntext-align:center;font-family: verdana,arial,sans-serif;font-size:30px;color:#333333;border-width: 1px;border-color: #666666; border-collapse: collapse;}\n\
h2{font-family: verdana,arial,sans-serif;font-size:20px;color:#333333;border-width: 1px;border-color: #666666;border-collapse: collapse;}\n\
table.summary { font-family: verdana,arial,sans-serif;font-size:11px;color:#333333;border-width: 1px;border-color: #666666;border-collapse: collapse;}\n\
table.gridtable {font-family: verdana,arial,sans-serif;font-size:11px;color:#333333;border-width: 1px;border-color: #666666;border-collapse: collapse;}\n\
table.gridtable th {border-width: 1px;padding: 8px;border-style: solid;border-color: #666666;background-color: #dedede;}\n\
table.gridtable td {border-width: 1px;padding: 8px;border-style: solid;border-color: #666666;background-color: #ffffff;}\n\
table .FAIL {color: Red;}\n\
table .PASS {color: Green;}\n</style>"
self.page.head+=css
utf8 = "<meta http-equiv=\"Content-Type\" content=\"text/html;charset="+ ec +"\" />"
self.page.head+=utf8
self.page << h1(h1title)
def addSummary(self, passed, failed, start, end):
"""
add summary html to page
:return:
"""
self.page << h2('Summary Information')
sumtable = self.page << table(cl='summary')
sumtable << tr(th('Total:',align='right') + td(int(passed) + int(failed)))
sumtable << tr(th('Passed:',align='right') + td(passed, cl='PASS'))
sumtable << tr(th('Fail:',align='right') + td(failed, cl='FAIL'))
sumtable << tr(th('Start Time:',align='right') + td(start))
sumtable << tr(th('End Time:',align='right') + td(end))
def addDetail(self, cases):
"""
add detail to page
:return:
"""
self.page << h2('Detail')
dtable = self.page << table(cl='gridtable')
thead = dtable << tr()
thead << th('#')
thead << th('Case Name', width="400px")
thead << th('Tags')
thead << th('Status')
thead << th('Message')
index = 1
for case in cases:
dtable << tr(td(index) + td(case[0]) + td(case[3]) + td(case[1], cl=case[1]) + td(case[2]))
index = index + 1
def writeTofile(self, report, ec='utf-8'):
"""
wirte page to html file
:return:
"""
self.page.printOut(report, ec)
if __name__ == '__main__':
main(sys.argv)
#gr = GenerateReport("Test Report")
# gr.get_title("c:/temp/ttt.xml")
3. pyh库文件修改
参见 文章http://blog.youkuaiyun.com/max229max/article/details/50695980
修改好的文件如下:
# coding=utf-8
# @file: pyh.py
# @purpose: a HTML tag generator
# @author: Emmanuel Turlay <turlay@cern.ch>
__doc__ = """The pyh.py module is the core of the PyH package. PyH lets you
generate HTML tags from within your python code.
See http://code.google.com/p/pyh/ for documentation.
"""
__author__ = "Emmanuel Turlay <turlay@cern.ch>"
__version__ = '$Revision: 43 $'
__date__ = '$Date$'
from sys import _getframe, stdout, modules, version
nOpen={}
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
nl = '\n'
doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
charset = '<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />\n'
tags = ['html', 'body', 'head', 'link', 'meta', 'div', 'p', 'form', 'legend',
'input', 'select', 'span', 'b', 'i', 'option', 'img', 'script',
'table', 'tr', 'td', 'th', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'fieldset', 'a', 'title', 'body', 'head', 'title', 'script', 'br', 'table']
selfClose = ['input', 'img', 'link', 'br']
class Tag(list):
tagname = ''
def __init__(self, *arg, **kw):
self.attributes = kw
if self.tagname :
name = self.tagname
self.isSeq = False
else:
name = 'sequence'
self.isSeq = True
self.id = kw.get('id', name)
#self.extend(arg)
for a in arg: self.addObj(a)
def __iadd__(self, obj):
if isinstance(obj, Tag) and obj.isSeq:
for o in obj: self.addObj(o)
else: self.addObj(obj)
return self
def addObj(self, obj):
if not isinstance(obj, Tag): obj = str(obj)
id=self.setID(obj)
setattr(self, id, obj)
self.append(obj)
def setID(self, obj):
if isinstance(obj, Tag):
id = obj.id
n = len([t for t in self if isinstance(t, Tag) and t.id.startswith(id)])
else:
id = 'content'
n = len([t for t in self if not isinstance(t, Tag)])
if n: id = '%s_%03i' % (id, n)
if isinstance(obj, Tag): obj.id = id
return id
def __add__(self, obj):
if self.tagname: return Tag(self, obj)
self.addObj(obj)
return self
def __lshift__(self, obj):
self += obj
return obj
def render(self):
result = ''
if self.tagname:
result = '<%s%s%s>' % (self.tagname, self.renderAtt(), self.selfClose()*' /')
if not self.selfClose():
for c in self:
if isinstance(c, Tag):
result += c.render()
else: result += c
if self.tagname:
result += '</%s>' % self.tagname
result += '\n'
return result
def renderAtt(self):
result = ''
for n, v in self.attributes.iteritems():
if n != 'txt' and n != 'open':
if n == 'cl': n = 'class'
result += ' %s="%s"' % (n, v)
return result
def selfClose(self):
return self.tagname in selfClose
def TagFactory(name):
class f(Tag):
tagname = name
f.__name__ = name
return f
thisModule = modules[__name__]
for t in tags: setattr(thisModule, t, TagFactory(t))
def ValidW3C():
out = a(img(src='http://www.w3.org/Icons/valid-xhtml10', alt='Valid XHTML 1.0 Strict'), href='http://validator.w3.org/check?uri=referer')
return out
class PyH(Tag):
tagname = 'html'
def __init__(self, name='MyPyHPage'):
self += head()
self += body()
self.attributes = dict(xmlns='http://www.w3.org/1999/xhtml', lang='en')
self.head += title(name)
def __iadd__(self, obj):
if isinstance(obj, head) or isinstance(obj, body): self.addObj(obj)
elif isinstance(obj, meta) or isinstance(obj, link): self.head += obj
else:
self.body += obj
id=self.setID(obj)
setattr(self, id, obj)
return self
def addJS(self, *arg):
for f in arg: self.head += script(type='text/javascript', src=f)
def addCSS(self, *arg):
for f in arg: self.head += link(rel='stylesheet', type='text/css', href=f)
def printOut(self,file='', ec='UTF-8'):
if file: f = open(file,'wb')
else: f = stdout
f.write(doctype.encode(ec))
f.write(self.render().encode(ec))
f.flush()
if file: f.close()
class TagCounter:
_count = {}
_lastOpen = []
for t in tags: _count[t] = 0
def __init__(self, name):
self._name = name
def open(self, tag):
if isLegal(tag):
self._count[tag] += 1
self._lastOpen += [tag]
def close(self, tag):
if isLegal(tag) and self._lastOpen[-1] == tag:
self._count[tag] -= 1
self._lastOpen.pop()
else:
print 'Cross tagging is wrong'
def isAllowed(self, tag, open):
if not open and self.isClosed(tag):
print 'TRYING TO CLOSE NON-OPEN TAG: %s' % tag
return False
return True
def isOpen(self, tag):
if isLegal(tag): return self._count[tag]
def isClosed(self, tag):
if isLegal(tag): return not self._count[tag]
def isLegal(tag):
if tag in tags: return True
else:
print 'ILLEGAL TAG: %s' % tag
return False
4. jenkins 中使用
执行用例在jenkins中的时候 邮件发送报告需要 gbk编码的,不然邮件内容会出现乱码,但是jenkins 里面的格式需要utf-8格式的,不然在jenkins上查看就是乱码,所以需要2份报告,一份gbk的,一份utf-8 的。
使用上面的代码生成
python Utils\generateRFreport.py report\output.xml report\emailreport.html gbk
python Utils\generateRFreport.py report\output.xml report\utfreport.html utf-8
copy report\emailreport.html emailreport.html
email 设置:
测试报告地址:<a href="${BUILD_URL}robot/report/utfreport.html">测试报告</a><br/><hr/>
测试日志地址:<a href="${BUILD_URL}robot/report/log.html">${BUILD_URL}robot/report/log.html</a><br/><hr/>
测试报告:<br/><hr/>
${FILE,path="emailreport.html"}
邮件效果图:

本文介绍如何通过Python的pyH库和Robot Framework的结果XML文件生成自定义的HTML报告。首先,需要安装pyH和lxml库。然后,根据XML文件以指定编码(如GBK或UTF-8)生成HTML报告。在Jenkins中,由于邮件和 Jenkins 查看报告需要不同的编码,所以需要创建两份报告以避免乱码问题。邮件设置中会链接到UTF-8编码的报告,而GBK编码的报告则用于邮件内容。
2921

被折叠的 条评论
为什么被折叠?



