本文介绍了特殊格式log日志生成自定义报告方法,项目地址
https://gitee.com/JasonZhu2021/reporter-tool-python
前言
如果在日常工作中,遇到一堆数据,但是想实现自动化将数据生成报告或者统计报表,这时候就需要掌握一些定制化输出报告的技能了;
模板文件
: 对于特定格式的报告输出,首先我们需要准备一个模板文件,在这个模板文件中,需要数据的内容用变量表示,而其他与数据无关的内容则事先编辑好(主题、外观、排版等);
数据文件
: 数据文件就是我们的数据来源,可以是txt文本流,可以是csv或者excel表格文件,或者其他常规数据文件;
解析器
: 解析器其实就是程序或者脚本,解析器的作用是将数据文件提取转换成特定的格式,这个特定的格式是模板文件所需要注入的;
定制化报表/报告的输出流程如下:
本文所介绍的是通用的方法,可以作为其他自动化报表输出的思路参考,本文中实际使用的工程代码以及测试文件将分享在gitee上;源码文章中的代码只是示例作用;想要熟练使用本文涉及的工具,可能要具备基本的一些技能:
Python 基本语法与基本应用
Mako文档阅读,了解Mako的语法和基本用法
1. 安装Python和Mako库
在Python环境下,安装和Mako库
pip install Mako
2. 自定义LOG字符流(示例)
以在板单元测试的输出日志为例: 单元测试的LOG字符流输出为“UNIT:"作为前缀:
以数字开头表示测试用例下测试步骤对应的信息,如:
前缀 | 分隔符 | 步骤号 | 时间戳 | 调用的API | 描述 | 结果 | 尾缀 |
---|---|---|---|---|---|---|---|
UNIT | : | 数字 | 浮点 | spi_flash_chip_id | Read chip id is c84018 | pass/fail | \r\n |
信息之间用逗号分隔符分隔,如: UNIT:0,0.1,spi_flash_chip_id,Read chip id is c84018,pass\r\n
前缀 | 分隔符 | 前缀 | 测试用例名称 | 尾缀 |
---|---|---|---|---|
UNIT | : | BEGIN | ABCDEF | \r\n |
表明测试用例开始执行
前缀 | 分隔符 | 前缀 | 测试用例名称 | 尾缀 |
---|---|---|---|---|
UNIT | : | END | ABCDEF | \r\n |
表明测试用例执行完毕
实际输出的txt文本
测试日志输出 unit_test 20241204 150444.txt
UNIT:BEGIN|FatFs Driver Test
INFO:>> Flash has no filesystem yet, formatting... <<
INFO:>> Flash already formatted. <<
UNIT:0|0.719|app_fatfs_mount|fatfs can be mounted successfully|pass
UNIT:1|0.729|f_open|create a new txt file|pass
UNIT:1|0.785|f_write|write context to txt file|pass
UNIT:1|0.936|f_close|close txt file|pass
UNIT:2|0.938|f_open|open a exist txt file|pass
UNIT:2|0.940|f_size|check file size 33|pass
UNIT:2|0.948|f_read|read context from opened file|pass
UNIT:2|0.950|f_close|close a opened file|pass
UNIT:3|0.952|-|check read content is write content|fail
INFO:test__Fatfs_Driver <!!!> failed: E:/MyWorkLog/MainLine2/Tools/12_PicoThermocouple/03_Embeddedsoftware/PicoTempratureMeter/unit/minunit-test-list.c:240:equal_r == FALSE
UNIT:4|0.961|app_fatfs_unmount|fatfs can be unmounted successfully|pass
UNIT:END|FatFs Driver Test
INFO:2 tests, 20 assertions, 1 failures
INFO:Finished in 0.967 seconds
3. 制作一个html报告的模板文件
模板文件可以从网上淘一个自己的喜欢的,当然,如果熟悉html也可以自己写一个;这里借用了别家html的报告稍加修改,效果大概是这样的。
测试用例下测试步骤的数据结构
这个报告的标准输出模板主体为5列表格,每一行代表了实际的测试流程项,由此我们可以用json格式先表示测试项这样的数据,以方便我们后续的代码实现:
{
"index": "0",
"time_stamp": "0.719",
"api": "app_fatfs_mount",
"description": "fatfs can be mounted successfully"
"result": "pass"
}
测试用例的数据结构
一个测试用例携带了用例信息,如样例所示,包含了测试开始时间、结束时间,测试用例标题等,一个测试用例会绑定测试步骤列表:
{
"index": "2",
"title": "Fatfs Driver Test",
"begin_time": "2024-12-04 15:04:44",
"end_time": "2024-12-04 15:04:44",
"result": "pass",
"steps": [{
"index": "0",
"time_stamp": "0.719",
"api": "app_fatfs_mount",
"description": "fatfs can be mounted successfully"
"result": "pass"
},{
"index": "1",
"time_stamp": "0.729",
"api": "f_open",
"description": "create a new txt file"
"result": "pass"
},
// ...
]
}
多个测试用例的信息即是上述结构以列表(数组)形式嵌套表述;
4. Python实现字符流解析
Python代码要实现的功能就是第二节约定的txt日志字符流文件按照特定格式解析,保存在字典数组嵌套数据里(第三节内容):
使用Python的话只需要少量的代码就可以实现解析,如上面的样例,打开txt文件,采用行读取的方式:
一次性读取所有行
逐行进行字符”:“分割,逃过前缀非”UNIT“的行
前缀为”UNIT“的行中,对后段进行字符”|“分割
"|"分割后的数据长度是固定的,如果长度是2,则表示测试用例的起始和结束;如果长度是5,则表示测试用例的测试流程信息
如果当前行是测试用例的起始,通过”BEGIN“判断,新建测试用例数据对象
如果当前行是测试流程信息,追加测试步骤信息
如果当前行是测试用例结束,通过”END“判断,追加测试用例下的完整测试步骤信息,随后将当前测试用例追加到用例列表
代码如下:
test_case_detail = {}
test_case_step_detail = []
with open(self.path + "/" + item,'r', encoding='UTF-8') as f:
self.log_lines = f.readlines()
for line in self.log_lines:
line_items = line.split(':')
if line_items[0].find("UNIT") != -1:
line_infos = line_items[1].split('|')
if len(line_infos) == 5:
test_case_step_detail.append({"index": line_infos[0], "time_stamp": line_infos[1], "api": line_infos[2], "description": line_infos[3], "result": line_infos[4]})
if line_infos[4].find("fail") != -1:
test_case_detail["result"] = False
else:
if line_infos[0].find("END") != -1:
test_case_detail["steps"] = test_case_step_detail
test_case_detail["begin_time_stamp"] = test_case_step_detail[0]["time_stamp"]
test_case_detail["end_time_stamp"] = test_case_step_detail[-1]["time_stamp"]
test_cases.append(test_case_detail)
if line_infos[0].find("BEGIN") != -1:
num_of_all_case += 1
test_case_detail = {"index": num_of_all_case, "title": line_infos[1], "description": line_infos[1], "result": True, "begin_time": test_time, "end_time": test_time}
test_case_step_detail = []
5. 基于html修改成生成报告的模板文件
我们以实际输出的report.html为模板,复制重命名为template.html,找到测试步骤对应的的内容:
...
<tr>
<td class="DefineCell">0.719</td>
<td class="NumberCell">0</td>
<td class="DefaultCell">app_fatfs_mount</td>
<td class="DefaultCell">fatfs can be mounted successfully</td>
<td class=PositiveResultCell>pass</td>
</tr>
...
在上面对应块的地方实现Mako的渲染脚本:
% for step in item["steps"]:
<%
resCellClass = "DefaultCell"
if step["result"] == "fail\n":
resCellClass = "NegativeResultCell"
elif step["result"] == "pass\n":
resCellClass = "PositiveResultCell"
endif
%>
<tr>
<td class="DefineCell">${step["time_stamp"]}</td>
<td class="NumberCell">${step["index"]}</td>
<td class="DefaultCell">${step["api"]}</td>
<td class="DefaultCell">${step["description"]}</td>
<td class=${resCellClass}>${step["result"]}</td>
</tr>
% endfor
不难发现,模板的意义就是替换掉要显示的数据内容,加以循环控制和逻辑判断;模板里的的变量,就是数据结构中的对应的内容;
6. 使用Mako对模板templete.html进行渲染
使用Mako库渲染输出报告,下面为示例代码:
import os
from mako.template import Template
from mako.runtime import Context
from io import StringIO
...
mytemplate = Template(filename='./template.html')
buf = StringIO()
ctx = Context(buf, **self.test_case_detail)
mytemplate.render_context(ctx)
with open("report.html",'w', encoding='UTF-8', newline="") as f:
f.write(buf.getvalue())
...
报告完整的效果是这样的:
标签:case,log,Python,html,detail,测试用例,time,test,line From: https://www.cnblogs.com/o-O-oO/p/18639730原创 Zhu 聪拌面搞嵌入式