python3 unittest+BeautifulReport单个进程输出多个测试报告

最近一个项目中需要由于输出的案例内容非常多(上万条),导致BeautifulReport输出的报告内容非常大(几百兆)。浏览器无法正常处理这么大的测试报告,就算打开了,也不方便阅读和处理,因此需要将报告分成多个输出。

经修改代码,发现单个进程内输出多个测试报告出现问题:第一个测试报告能正常数据对应加入到unittest.testSuite的中的案例,但是后面每一个报告会输出前面所有报告的案例。

如果进行多进程改造的话改造量又比较大,因此还是采用的单进程多线程方案。

经排查发现是BeautifulReport中用于存放测试结果的变量FIELDS是一个全局变量,因此就算新建多个BeautifulReport的实例也没办法正常输出每个实例中unittest.testSuite的报告内容,需修改BeautifulReport中关于FIELDS变量的使用。

结合unittest和BeautifulReport测试api接口的的使用代码如下:

 1 #!/usr/bin/python
 2 import os,sys
 3 from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, as_completed
 4 from multiprocessing import Process, Lock, Queue
 5 import unittest
 6 import time
 7 import  copy
 8 sys.path.append(r'')
 9 
10 from BeautifulReport import BeautifulReport
11 
12 def output_report(testsuit, filename, interface_name):
13     try:
14         log.info(f"output_report filename:{filename} interface_name:{interface_name}")
15         now = time.strftime("%Y-%m-%d_%H_%M_%S")
16         result = BeautifulReport(testsuit)
17         result.report(filename=now + "_" + filename + f"_{interface_name}" +'_测试报告.html', description='测试报告', log_path='../report')
18 
19         log.info(f"output_report filename:{filename} interface_name:{interface_name} over")
20         print(f"******output_report filename:{filename} interface_name:{interface_name} over******")
21 
22         return {'result':True}
23     except Exception as e:
24         print(f"output_report filename:{filename} interface_name:{interface_name} exception! error_info:{e}")
25         return {'result':False, 'interface_name':interface_name, 'error_info':e}
26 
27 def demo_run(cases):
28     #省略一些准备代码
29     #......
30     
31     #多线程执行api接口测试任务
32     with ThreadPoolExecutor(max_workers=thread_num) as ts:
33         for case in cases:
34             if len(case['req']) == 1 and len(case['file_rsp']) != 1:
35                 log.error(f"case_id:{case['TestCaseID']} file_rsp len:{len(case['file_rsp'])} != 1 error!")
36                 continue
37             task.append(ts.submit(test.send_request, api, case))
38 
39     #收集任务返回结果
40     result_msg_list = [future.result() for future in as_completed(task)]
41 
42     #将执行结果加入到testsuit中
43     #todo:输出报告如果太大内容太多导致输出时间长,则需要改造为多线程处理
44     test_suit_dict = {}
45     result_msg_list.sort(key=lambda x:x["TestCaseID"])
46     for result_msg in result_msg_list:
47         interface_name = result_msg['interface_name']
48         log.info(f"TestCaseID:{result_msg['TestCaseID']} interface_name:{interface_name} func_name:{result_msg['func_name']}")
49 
50         if interface_name in test_suit_dict:
51             testsuit = test_suit_dict[interface_name]
52             testsuit.addTest(ParametrizedTestCase.parametrize(Test_Cash_Order_Api, "test_case_ATP", param=(result_msg,key)))
53         else:
54             testsuit = unittest.TestSuite()
55             test_suit_dict[interface_name] = testsuit
56             testsuit.addTest(ParametrizedTestCase.parametrize(Test_Cash_Order_Api, "test_case_ATP", param=(result_msg,key)))
57 
58     #检查输出报告目录是否存在
59     if os.path.exists("../report") != True:
60         os.makedirs("../report")
61 
62     #BeautifulReport输出testsuit中的比对结果
63     #注意这里输出多个测试报告需改造Beautifulreport源码中存放测试结果的FIELDS的使用,否则无法正常输出多个测试报告(每个测试报告会输出前面加入的测试集的结果)
64     #因为Beautifulreport源码中用的FIELDS变量是个全局变量,单个进程中加入到FIELDS中的测试结果会不断累计。需要将FIELDS变为类的成员变量。这样新建多个BeautifulReport的实例才能正常输出多个测试报告
65     #另外使用多进程输出多个测试报告比较难实现。需要多进程中使用功能的内容都是进程安全的,涉及到的logging等组件的使用都需要做改造。这里直接改造Beautifulreport中的FIELDS较为容易实现
66     for interface_name, testsuit in test_suit_dict.items():
67         log.info(f"add process filename:{filename} interface_name:{interface_name}")
68         output_report(testsuit, filename, interface_name)

 

BeautifulReport中改造FIELDS的代码如下。左侧为改造前代码,右侧为改造后代码。思路是将FIELDS变为类的成员变量,而不是使用全局变量。

基类中初始化FIELDS为类成员变量:

输出的报告内容使用成员变量存储:

 

posted @ 2024-07-31 09:14  youxueluffy  阅读(63)  评论(0)    收藏  举报