概要
作为一个测试工程师,编写功能测试报告然后发送邮件是工作内容中必不可少的一个环节。可是在现在项目迭代很快的时代,我们在编写测试报告花费了不少时间,所以为了节省时间,提高工作效率这个需求,通过代码自动生成功能测试时报告的想法油然而生!
最终效果如下:
整体架构流程
1.数据采集:使用Python的jira库和官方文档获取对应BUG清单
2.数据转化:Pandas和pyploty库生成html代码的BUG表格和对应的饼图和条形图
3.AI分析:将采集到的数据通过调用讯飞星火的api来进行分析
4.邮件发送:将对应模板的html通过jinja将对应数据塞入,最终通过email库发送给应查看报告的人
实现细节
html模板设计
文档化的功能测试报告,有点经验的测试工程师都能实现。通过代码生成的报告,我建议是实现html形式的,产出的效果会好看很多。(只是可惜邮件只支持纯html文本,不支持js,css所以没有直接打开html在浏览器上的效果好)
在没有很丰富的html的基础下,通过查阅资料得知可以通过markdown来转化html。
# {
{title}}
<img src="{
{url}"></img>
## 测试目的
{
{description}}
## 测试情况
{te1}
### BUG统计
{
{chart}}
### BUG列表
{
{table}}
## 测试结论
测试情况:{
{text}}
总体测试情况:{
{text1}}
测试负责人:{
{name}}
生成的html文件:
数据采集
公司使用的项目管理平台是jira,所以目前是通过python的第三方jira库和官方jira的restful文档来采集对应数据。
首先在cmd命令行安装对应依赖
pip install jira
通过查阅网上的相关文档可得,jira库的登录方式是通过cookie方式登录,对外提供的对象是JIRA
那么我们只需要新建该实例,即可连接成功。
考虑到以后可能会连接禅道等其他项目管理平台,这边使用桥接模式来设计该块代码:
class exportMeta(abc.ABC):
'''
数据获取的抽象类
'''
@abc.abstractmethod
def GetData(self):
pass
class Export(exportMeta):
'''
数据获取的实现类
'''
def __init__(self, type_export):
self.export = type_export
def GetData(self):
'''
获取数据
:return:
'''
self.export.GetData()
# 封装一下第三方库
class jira_export(exportMeta):
def __init__(self, username: str, password: str, url: str):
self.auth = (username, password)
self.url = url
self.option = JIRA(server=self.url, auth=self.auth)
self.notifyList = []
Python中的第三方JIRA库中查询对应任务清单是通过key来查询,我们将对应的key放入其中对应的方法所需参数就可以获取到该任务清单相关的信息。
JIRA(server=self.url, auth=self.auth).issue(key)
通过阅读源码可知,返回的对象是一个名为Issue的实例,该实例有option、session、raw、fields、id、key参数
通过打印print对应实例的每个参数内的信息,可得raw中包含我们所需的内容。
那么思路:通过建造者模式去拆分这个Issue的实例,来获取每个我们所需的参数,将这些所需参数放入我们编写的一个对象中进行实例化
# Todo:任务类
class Issue():
def __init__(self, obj: jira.Issue):
self.key = None
self.data = obj
self.type = None
self.title = None
self.description = None
self.url = None
self.marker = None
self.startTime = None
self.endTime = None
self.subtasks = None
def __dict__(self):
return {
"url": self.url, "title": self.title, "key": self.key, "type": self.type, "description": self.description,
"marker": self.marker, "startTime": self.startTime, "endTime": self.endTime}
def __str__(self):
return f"标题为{
self.title},地址为{
self.url}"
def __getattr__(self, name):
print(f"查询对应属性{
name}失败")
return None
# Todo:任务处理类
class IssueExplaner:
'''
问题消息处理者(建造者模式中的厨师角色)
'''
def __init__(self, list):
self.list = list
def explanIssue(self, obj, option):
'''
以建造者模式处理issue类的数据,若是bug类型的type直接消息推送
:return:
'''
[i(obj) for i in
[self.set_title, self.set_key, self.set_type, self.set_description, self.set_url, self.set_marker, self.set_startTime,
self.set_endTime, self.set_subtasks]]
if obj.type == "BUG":
for i in self.list:
i.notify(obj.subtasks, option)
def set_subtasks(self, issue):
issue.subtasks = issue.data.fields.subtasks
def set_type(self, issue):
issue.type = issue.data.fields.issuetype.name
def set_title(self, issue):
summary = issue.data.fields.summary
result = re.split(r'】', summary)[1]
issue.title = result
def set_description(self, issue):
description = des if (des := issue.data.fields.description) else ""
issue.description = description
def set_url(self, issue):
url = re.findall(r'[htps0-9.:/]+', issue.data.raw.get("self"))[0] + f"browse/