This commit is contained in:
zhaoyafan 2022-08-21 02:25:55 +08:00
parent dd67ce3cf4
commit 82a5910b95
11 changed files with 365 additions and 2395 deletions

View File

@ -845,7 +845,7 @@ class _TestResult(unittest.TestResult):
def startTest(self, test): def startTest(self, test):
# 单条用例执行开始前的动作 # 单条用例执行开始前的动作
if self.verbosity >= 2: if self.verbosity >= 2:
sys.stderr.write(_Color(fc=39, bc=4, bo=1, text='%-79s' % ('%04d Testing...' % (len(self.result) + 1))) + "\n") sys.stderr.write(_Color(fc=39, bc=4, bo=1, text='%-79s' % ('%04d @ Testing...' % (len(self.result) + 1))) + "\n")
super().startTest(test) super().startTest(test)
if self.verbosity >= 0: if self.verbosity >= 0:
self.loggerStream = io.StringIO() self.loggerStream = io.StringIO()
@ -1072,7 +1072,7 @@ class HTMLTestRunner(Template_mixin):
sys.stderr.write(_Color(fc=37, bo=0, text=str(i + 1) + '. ' + value[1][i]) + "\n") sys.stderr.write(_Color(fc=37, bo=0, text=str(i + 1) + '. ' + value[1][i]) + "\n")
if len(casesort.keys()): if len(casesort.keys()):
sys.stderr.write(_Color(fc=34, bo=1, sys.stderr.write(_Color(fc=34, bo=1,
text='%-16s' % ('用例集合') + "\t" + text='%-22s' % ('用例集合') + "\t" +
'%-4s' % ('总计') + "\t" + '%-4s' % ('总计') + "\t" +
'%-4s' % ('通过') + "\t" + '%-4s' % ('通过') + "\t" +
'%-4s' % ('失败') + "\t" + '%-4s' % ('失败') + "\t" +
@ -1080,12 +1080,12 @@ class HTMLTestRunner(Template_mixin):
'%-8s' % ('耗时') + "\t") + "\n") '%-8s' % ('耗时') + "\t") + "\n")
for key, value in casesort.items(): for key, value in casesort.items():
sys.stderr.write(_Color(fc=37, bo=0, sys.stderr.write(_Color(fc=37, bo=0,
text='%-16s' % (str(key)) + "\t" + text='%-22s' % (str(key)) + "\t" +
'%-4s' % (str(value["p"] + value["f"] + value["e"])) + "\t" + '%-4s' % (str(value["p"] + value["f"] + value["e"])) + "\t" +
'%-4s' % (str(value["p"])) + "\t" + '%-4s' % (str(value["p"])) + "\t" +
'%-4s' % (str(value["f"])) + "\t" + '%-4s' % (str(value["f"])) + "\t" +
'%-4s' % (str(value["e"])) + "\t" + '%-4s' % (str(value["e"])) + "\t" +
'%.3f' % (round(value["d"], 3)) + '' + "\t" '%-8s' % ('%.3f' % (round(value["d"], 3)) + '') + "\t"
) + "\n") ) + "\n")
return result return result

File diff suppressed because it is too large Load Diff

View File

@ -229,7 +229,7 @@
series: [{ series: [{
name: '通过', name: '通过',
color: '#64bb64', color: '#64bb64',
data: [2] data: [4]
}, { }, {
name: '失败', name: '失败',
color: '#f16d7e', color: '#f16d7e',
@ -291,7 +291,7 @@
innerSize: '80%', innerSize: '80%',
name: '比例', name: '比例',
data: [ data: [
['通过', 2], ['失败', 0], ['错误', 0] ['通过', 4], ['失败', 0], ['错误', 0]
] ]
}] }]
}, function(c) { }, function(c) {
@ -438,11 +438,11 @@ function html_escape(s) {
<div id="testinfo" style="max-width: 360px; width: auto; float: left;"> <div id="testinfo" style="max-width: 360px; width: auto; float: left;">
<h1 style="margin: 5px 0px 10px 0px; font-family: Microsoft YaHei;">测试报告</h1> <h1 style="margin: 5px 0px 10px 0px; font-family: Microsoft YaHei;">测试报告</h1>
<p class='attribute'><strong>开始时间 : </strong> 2022-08-16 18:41:36</p> <p class='attribute'><strong>开始时间 : </strong> 2022-08-21 02:25:13</p>
<p class='attribute'><strong>合计耗时 : </strong> 00:00:00</p> <p class='attribute'><strong>合计耗时 : </strong> 00:00:01</p>
<p class='attribute'><strong>测试结果 : </strong> 总共 2通过 2,失败 0错误 0通过率 100.00%</p> <p class='attribute'><strong>测试结果 : </strong> 总共 4通过 4,失败 0错误 0通过率 100.00%</p>
<p class='attribute'><strong>失败用例 : </strong></p> <p class='attribute'><strong>失败用例 : </strong></p>
@ -458,10 +458,10 @@ function html_escape(s) {
<div style="width: auto; clear: both;"> <div style="width: auto; clear: both;">
<p id='show_detail_line'> <p id='show_detail_line'>
<a class="btn btn-primary" href='javascript:showCase(0)'>概要 100.00%</a> <a class="btn btn-primary" href='javascript:showCase(0)'>概要 100.00%</a>
<a class="btn btn-success" href='javascript:showCase(2)'>通过 2</a> <a class="btn btn-success" href='javascript:showCase(2)'>通过 4</a>
<a class="btn btn-danger" href='javascript:showCase(1)'>失败 0</a> <a class="btn btn-danger" href='javascript:showCase(1)'>失败 0</a>
<a class="btn btn-warning" href='javascript:showCase(3)'>错误 0</a> <a class="btn btn-warning" href='javascript:showCase(3)'>错误 0</a>
<a class="btn btn-info" href='javascript:showCase(4)'>全部 2</a> <a class="btn btn-info" href='javascript:showCase(4)'>全部 4</a>
</p> </p>
</div> </div>
<table id='result_table' class="table table-condensed table-bordered table-hover"> <table id='result_table' class="table table-condensed table-bordered table-hover">
@ -489,24 +489,25 @@ function html_escape(s) {
<tr class='passClass warning'> <tr class='passClass warning'>
<td>测试用例</td> <td>测试用例</td>
<td></td> <td></td>
<td class="text-center">2</td> <td class="text-center">4</td>
<td class="text-center">2</td> <td class="text-center">4</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center">0.238</td> <td class="text-center">1.172</td>
<td class="text-center"><a href="javascript:showClassDetail('c1',2)" class="detail" id='c1'>查看全部</a></td> <td class="text-center"><a href="javascript:showClassDetail('c1',4)" class="detail" id='c1'>查看全部</a></td>
</tr> </tr>
<tr id='pt1_1' class='hiddenRow'> <tr id='pt1_1' class='hiddenRow'>
<td class='passedCase' style="vertical-align: middle"><div class='testcase'>test0001_lookup2</div></td> <td class='passedCase' style="vertical-align: middle"><div class='testcase'>test0001_login2</div></td>
<td style="vertical-align: middle"></td> <td style="vertical-align: middle">P0:测试正常登录</td>
<td colspan='5' align='center'> <td colspan='5' align='center'>
<button id='btn_pt1_1' type="button" class="btn btn-xs" data-toggle="collapse" data-target='#div_pt1_1'>通过</button> <button id='btn_pt1_1' type="button" class="btn btn-xs" data-toggle="collapse" data-target='#div_pt1_1'>通过</button>
<div id='div_pt1_1' class="collapse in"> <div id='div_pt1_1' class="collapse in">
<pre style="text-align:left;font-family:monospace;font-size:12px;color:#119611">pt1_1: <pre style="text-align:left;font-family:monospace;font-size:12px;color:#119611">pt1_1:
2022-08-16 18:41:36,269 - D: 2022-08-21 02:25:13,190 - D: test0001_login2
2022-08-21 02:25:13,469 - D:
[-&gt;] [-&gt;]
GET /iplookup?ip=127.0.0.1 HTTP/1.1 GET / HTTP/1.1
Host: www.fanscloud.net Host: www.fanscloud.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Accept-Encoding: gzip, deflate Accept-Encoding: gzip, deflate
@ -517,14 +518,20 @@ Connection: keep-alive
[&lt;-] [&lt;-]
HTTP/1.1 200 OK HTTP/1.1 200 OK
Server: nginx Server: nginx
Date: Tue, 16 Aug 2022 10:41:36 GMT Date: Sat, 20 Aug 2022 18:25:13 GMT
Content-Type: text/html; charset=utf-8 Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked Transfer-Encoding: chunked
Connection: keep-alive Connection: keep-alive
Vary: Accept-Encoding Vary: Accept-Encoding
Content-Encoding: gzip Content-Encoding: gzip
127.0.0.1 保留地址 &lt;!doctype html&gt;
&lt;html lang="zh-cn"&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&gt;
&lt;title&gt;阿凡的Blog🌿&lt;/title&gt;
&lt;meta name="keywo ... [MORE 98% OMITTED HERE]
</pre> </pre>
</div> </div>
</td> </td>
@ -532,15 +539,16 @@ Content-Encoding: gzip
</tr> </tr>
<tr id='pt1_2' class='hiddenRow'> <tr id='pt1_2' class='hiddenRow'>
<td class='passedCase' style="vertical-align: middle"><div class='testcase'>test0002_lookup2</div></td> <td class='passedCase' style="vertical-align: middle"><div class='testcase'>test0002_login4</div></td>
<td style="vertical-align: middle"></td> <td style="vertical-align: middle">P0:测试正常登录</td>
<td colspan='5' align='center'> <td colspan='5' align='center'>
<button id='btn_pt1_2' type="button" class="btn btn-xs" data-toggle="collapse" data-target='#div_pt1_2'>通过</button> <button id='btn_pt1_2' type="button" class="btn btn-xs" data-toggle="collapse" data-target='#div_pt1_2'>通过</button>
<div id='div_pt1_2' class="collapse in"> <div id='div_pt1_2' class="collapse in">
<pre style="text-align:left;font-family:monospace;font-size:12px;color:#119611">pt1_2: <pre style="text-align:left;font-family:monospace;font-size:12px;color:#119611">pt1_2:
2022-08-16 18:41:36,342 - D: 2022-08-21 02:25:13,470 - D: test0002_login4
2022-08-21 02:25:13,760 - D:
[-&gt;] [-&gt;]
GET /iplookup?ip=127.0.0.1 HTTP/1.1 GET / HTTP/1.1
Host: www.fanscloud.net Host: www.fanscloud.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Accept-Encoding: gzip, deflate Accept-Encoding: gzip, deflate
@ -551,17 +559,37 @@ Connection: keep-alive
[&lt;-] [&lt;-]
HTTP/1.1 200 OK HTTP/1.1 200 OK
Server: nginx Server: nginx
Date: Tue, 16 Aug 2022 10:41:37 GMT Date: Sat, 20 Aug 2022 18:25:13 GMT
Content-Type: text/html; charset=utf-8 Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked Transfer-Encoding: chunked
Connection: keep-alive Connection: keep-alive
Vary: Accept-Encoding Vary: Accept-Encoding
Content-Encoding: gzip Content-Encoding: gzip
127.0.0.1 保留地址 &lt;!doctype html&gt;
2022-08-16 18:41:36,364 - D: &lt;html lang="zh-cn"&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&gt;
&lt;title&gt;阿凡的Blog🌿&lt;/title&gt;
&lt;meta name="keywo ... [MORE 98% OMITTED HERE]
</pre>
</div>
</td>
<td class='passedCase' style="vertical-align: middle"></td>
</tr>
<tr id='pt1_3' class='hiddenRow'>
<td class='passedCase' style="vertical-align: middle"><div class='testcase'>test0003_login1</div></td>
<td style="vertical-align: middle">P1:测试正常登录</td>
<td colspan='5' align='center'>
<button id='btn_pt1_3' type="button" class="btn btn-xs" data-toggle="collapse" data-target='#div_pt1_3'>通过</button>
<div id='div_pt1_3' class="collapse in">
<pre style="text-align:left;font-family:monospace;font-size:12px;color:#119611">pt1_3:
2022-08-21 02:25:13,761 - D: test0003_login1
2022-08-21 02:25:14,047 - D:
[-&gt;] [-&gt;]
GET /iplookup?ip=127.0.0.2 HTTP/1.1 GET / HTTP/1.1
Host: www.fanscloud.net Host: www.fanscloud.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Accept-Encoding: gzip, deflate Accept-Encoding: gzip, deflate
@ -572,14 +600,61 @@ Connection: keep-alive
[&lt;-] [&lt;-]
HTTP/1.1 200 OK HTTP/1.1 200 OK
Server: nginx Server: nginx
Date: Tue, 16 Aug 2022 10:41:37 GMT Date: Sat, 20 Aug 2022 18:25:14 GMT
Content-Type: text/html; charset=utf-8 Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked Transfer-Encoding: chunked
Connection: keep-alive Connection: keep-alive
Vary: Accept-Encoding Vary: Accept-Encoding
Content-Encoding: gzip Content-Encoding: gzip
127.0.0.2 保留地址 &lt;!doctype html&gt;
&lt;html lang="zh-cn"&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&gt;
&lt;title&gt;阿凡的Blog🌿&lt;/title&gt;
&lt;meta name="keywo ... [MORE 98% OMITTED HERE]
</pre>
</div>
</td>
<td class='passedCase' style="vertical-align: middle"></td>
</tr>
<tr id='pt1_4' class='hiddenRow'>
<td class='passedCase' style="vertical-align: middle"><div class='testcase'>test0004_login3</div></td>
<td style="vertical-align: middle">P1:测试正常登录</td>
<td colspan='5' align='center'>
<button id='btn_pt1_4' type="button" class="btn btn-xs" data-toggle="collapse" data-target='#div_pt1_4'>通过</button>
<div id='div_pt1_4' class="collapse in">
<pre style="text-align:left;font-family:monospace;font-size:12px;color:#119611">pt1_4:
2022-08-21 02:25:14,048 - D: test0004_login3
2022-08-21 02:25:14,363 - D:
[-&gt;]
GET / HTTP/1.1
Host: www.fanscloud.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
[&lt;-]
HTTP/1.1 200 OK
Server: nginx
Date: Sat, 20 Aug 2022 18:25:14 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Content-Encoding: gzip
&lt;!doctype html&gt;
&lt;html lang="zh-cn"&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&gt;
&lt;title&gt;阿凡的Blog🌿&lt;/title&gt;
&lt;meta name="keywo ... [MORE 98% OMITTED HERE]
</pre> </pre>
</div> </div>
</td> </td>
@ -588,11 +663,11 @@ Content-Encoding: gzip
<tr id='total_row' class="text-center active"> <tr id='total_row' class="text-center active">
<td colspan='2'>总计</td> <td colspan='2'>总计</td>
<td>2</td> <td>4</td>
<td>2</td> <td>4</td>
<td>0</td> <td>0</td>
<td>0</td> <td>0</td>
<td>0.238</td> <td>1.172</td>
<td>通过100.00%</td> <td>通过100.00%</td>
</tr> </tr>
</table> </table>

View File

@ -1,6 +1,5 @@
import time
import unittest import unittest
from typing import Any, Text, Dict, List from argparse import ArgumentParser
from Business.Class.HTMLTestRunner import HTMLTestRunner from Business.Class.HTMLTestRunner import HTMLTestRunner
from Business.Class.ExcelUtils import * from Business.Class.ExcelUtils import *
from Business.Class.JsonOrYaml import * from Business.Class.JsonOrYaml import *
@ -40,18 +39,18 @@ class ReList(list):
return ReDict(r) return ReDict(r)
else: else:
return ReText(r) return ReText(r)
except: except Exception:
return __class__() return __class__()
else: else:
try: try:
return reparse(item, str(self.__str__())) return reparse(item, str(self.__str__()))
except: except Exception:
return ReText('') return ReText('')
def __str__(self): def __str__(self):
try: try:
return ReText(self[0]) return ReText(self[0])
except: except Exception:
return ReText('') return ReText('')
@ -71,7 +70,7 @@ class ReDict(dict):
else: else:
try: try:
return reparse(item, str(self.__str__())) return reparse(item, str(self.__str__()))
except: except Exception:
return ReText('') return ReText('')
def __str__(self): def __str__(self):
@ -80,7 +79,7 @@ class ReDict(dict):
return ReText(self.__getitem__('_')) return ReText(self.__getitem__('_'))
else: else:
return json_encode(self, indent=None, unicode=False) return json_encode(self, indent=None, unicode=False)
except: except Exception:
return ReText('') return ReText('')
def update(self, *args, **kwargs): def update(self, *args, **kwargs):
@ -104,7 +103,15 @@ class TestCase:
_g = {} _g = {}
_l = {} _l = {}
def __init__(self, workbook: Text, testlves: List, cases: Text, mysql: Text, httpc: Text): def __del__(self):
if self.write_test_result:
self.c_excel_object.save()
self.b_excel_object.save()
self.h_excel_object.save()
def __init__(self, workbook: str, testcase: str, module: str = None, testlves=None, organize_mode=None, write_test_result=None):
if isinstance(testlves, str):
testlves = [value.strip() for value in list(filter(lambda x: x, testlves.split(',')))]
tabs = { tabs = {
"HTTPConf": { "HTTPConf": {
"fixed": {}, "fixed": {},
@ -181,11 +188,11 @@ class TestCase:
for k, v in tabs.items(): for k, v in tabs.items():
match k: match k:
case 'TestCase': case 'TestCase':
sheet = cases sheet = testcase
case 'DataBase': case 'DataBase':
sheet = mysql sheet = '请求配置'
case 'HTTPConf': case 'HTTPConf':
sheet = httpc sheet = '请求配置'
case _: case _:
continue continue
self.init.select(sheet) self.init.select(sheet)
@ -200,19 +207,19 @@ class TestCase:
data[k]['ocell'] = read_view_dict( data[k]['ocell'] = read_view_dict(
filename=workbook, sheet=sheet, area=v['views'], fields=v['field'], auto_truncate=True, object_cell=True) filename=workbook, sheet=sheet, area=v['views'], fields=v['field'], auto_truncate=True, object_cell=True)
self.case = cases self.module_name = module
self.write_test_result = write_test_result
self.case = '%s_%s' % (testcase, module) if module else testcase
self.dirs = os.path.dirname(workbook) self.dirs = os.path.dirname(workbook)
self.request = Request() self.request = Request()
self.session = Session() self.session = Session()
self.h = DataFrame(data['HTTPConf']['views']['data']) self.h_vc = DataFrame(data['HTTPConf']['views']['data']).reset_index(drop=False, inplace=False)
self.b = DataFrame(data['DataBase']['views']['data']) self.b_vc = DataFrame(data['DataBase']['views']['data']).reset_index(drop=False, inplace=False)
self.c = DataFrame(data['TestCase']['views']['data']) self.c_vc = DataFrame(data['TestCase']['views']['data']).reset_index(drop=False, inplace=False)
self.h_oc = DataFrame(data['HTTPConf']['ocell']['data']).reset_index(drop=False, inplace=False)
self.h_ocell = DataFrame(data['HTTPConf']['ocell']['data']) self.b_oc = DataFrame(data['DataBase']['ocell']['data']).reset_index(drop=False, inplace=False)
self.b_ocell = DataFrame(data['DataBase']['ocell']['data']) self.c_oc = DataFrame(data['TestCase']['ocell']['data']).reset_index(drop=False, inplace=False)
self.c_ocell = DataFrame(data['TestCase']['ocell']['data'])
self.h_excel_object = data['HTTPConf']['ocell']['excel_object'] self.h_excel_object = data['HTTPConf']['ocell']['excel_object']
self.b_excel_object = data['DataBase']['ocell']['excel_object'] self.b_excel_object = data['DataBase']['ocell']['excel_object']
self.c_excel_object = data['TestCase']['ocell']['excel_object'] self.c_excel_object = data['TestCase']['ocell']['excel_object']
@ -238,6 +245,14 @@ class TestCase:
self.pre_executed_list = [] self.pre_executed_list = []
match str(organize_mode).upper():
case 'BY_ORDER' | '1' | '10':
self.organize_mode = 10
case 'BY_LEVEL' | '2' | '20':
self.organize_mode = 20
case _:
self.organize_mode = 10
def _set_levels(self, level_list=None): def _set_levels(self, level_list=None):
if isinstance(level_list, (list, tuple, str)): if isinstance(level_list, (list, tuple, str)):
self.leve_list = level_list self.leve_list = level_list
@ -251,7 +266,7 @@ class TestCase:
return 10 return 10
case '前置用例': case '前置用例':
return 20 return 20
case _: case '关闭用例' | _:
return 50 return 50
@staticmethod @staticmethod
@ -259,7 +274,7 @@ class TestCase:
match value: match value:
case '全局一次': case '全局一次':
return 10 return 10
case _: case '总是执行' | _:
return 50 return 50
@staticmethod @staticmethod
@ -332,7 +347,7 @@ class TestCase:
for variable_name in re.findall('\${(.*?)}', text): for variable_name in re.findall('\${(.*?)}', text):
try: try:
value = vars_dict[variable_name] value = vars_dict[variable_name]
except: except Exception:
value = '' value = ''
if self._is_nan(value) or value is None: if self._is_nan(value) or value is None:
value = '' value = ''
@ -398,7 +413,7 @@ class TestCase:
match domain: match domain:
case 'locals': case 'locals':
domain = self._l domain = self._l
case 'global': case 'global' | _:
domain = self._g domain = self._g
vars_dict = {k if not isinstance(k, str) else k.lower(): v for k, v in vars_dict.items()} vars_dict = {k if not isinstance(k, str) else k.lower(): v for k, v in vars_dict.items()}
import re import re
@ -455,12 +470,13 @@ class TestCase:
actual = str(actual) actual = str(actual)
assert actual == expect, '%s != %s' % (actual, expect) assert actual == expect, '%s != %s' % (actual, expect)
def _test_unit(self, data, main=None): def _test_unit(self, func_name, data, main=None):
log.d(func_name)
if data['HTTPUri']: if data['HTTPUri']:
if not data['HTTPChannel']: if not data['HTTPChannel']:
raise Exception('channel not set') raise Exception('channel not set')
from pandas.core.frame import DataFrame from pandas.core.frame import DataFrame
http = self.h.where((self.h['Name'] == data['HTTPChannel']), inplace=False).dropna(how='all').reset_index(drop=True, inplace=False).loc[0].to_dict() http = self.h_vc.where((self.h_vc['Name'] == data['HTTPChannel']), inplace=False).dropna(how='all').reset_index(drop=True, inplace=False).loc[0].to_dict()
res_kwargs = { res_kwargs = {
'method': data['HTTPMethod'], 'method': data['HTTPMethod'],
'url': self._sub_variable_auto((http['Scheme'] or 'http').lower() + '://' + (http['Host'] or '') + data['HTTPUri']), 'url': self._sub_variable_auto((http['Scheme'] or 'http').lower() + '://' + (http['Host'] or '') + data['HTTPUri']),
@ -493,7 +509,7 @@ class TestCase:
hvar = { hvar = {
'Status': ReText(res['status'] or ''), 'Status': ReText(res['status'] or ''),
'Reason': ReText(res['reason'] or ''), 'Reason': ReText(res['reason'] or ''),
'Header': ReDict(res['header'] or {}).update({'_': "\n".join([k + ': ' + (v or '') for k, v in dict(res['header'] or {}).items()])}), 'Header': ReDict(res['header'] or {}).update({'_': "\n".join([k + ':' + "\x20" + (v or '') for k, v in dict(res['header'] or {}).items()])}),
'Cookie': ReDict(res['cookie'] or {}).update({'_': "; ".join([k + '=' + (v or '') for k, v in dict(res['cookie'] or {}).items()])}), 'Cookie': ReDict(res['cookie'] or {}).update({'_': "; ".join([k + '=' + (v or '') for k, v in dict(res['cookie'] or {}).items()])}),
'Body': ReText(res['text'] or ''), 'Body': ReText(res['text'] or ''),
'Json': ReDict(res['json'] or {}), 'Json': ReDict(res['json'] or {}),
@ -510,7 +526,7 @@ class TestCase:
if data['BaseSql']: if data['BaseSql']:
if not data['BaseChannel']: if not data['BaseChannel']:
raise Exception('channel not set') raise Exception('channel not set')
base = self.b.where((self.b['Name'] == data['BaseChannel']), inplace=False).dropna(how='all').reset_index(drop=True, inplace=False).loc[0].to_dict() base = self.b_vc.where((self.b_vc['Name'] == data['BaseChannel']), inplace=False).dropna(how='all').reset_index(drop=True, inplace=False).loc[0].to_dict()
conn = MySQL().connect( conn = MySQL().connect(
host=base['Host'], port=int(base['Port']), host=base['Host'], port=int(base['Port']),
user=base['User'], password=base['Password'], database=base['Database'], charset=base['Charset'], cursor='Dict' user=base['User'], password=base['Password'], database=base['Database'], charset=base['Charset'], cursor='Dict'
@ -527,115 +543,211 @@ class TestCase:
{'expect': data['BaseAssertRows'], 'actual': bvar['Rows']}, {'expect': data['BaseAssertRows'], 'actual': bvar['Rows']},
], self.assert_list) ], self.assert_list)
def test(self, index, update_global=None, update_locals=None): def test(self, index, update_global=None, update_locals=None, func_name=''):
if isinstance(update_global, (dict,)): if isinstance(update_global, (dict,)):
self._g.update(update_global) self._g.update(update_global)
if isinstance(update_locals, (dict,)): if isinstance(update_locals, (dict,)):
self._l.update(update_locals) self._l.update(update_locals)
def write_test_result(excel, object_cell, test_result): def write_test_result(object_cell, test_result):
for cell, value in [[object_cell['TestResult'], test_result], [object_cell['TestTime'], time_strftime(fmt='%Y-%m-%d %H:%M:%S')]]: for cell, new_cell_value in [[object_cell['TestResult'], test_result], [object_cell['TestTime'], time_strftime(fmt='%Y-%m-%d %H:%M:%S')]]:
cell.value = value cell.value = new_cell_value
excel.save()
main_case_object_cell = self.c_ocell.loc[index].to_dict() main_case_object_cell = self.c_oc.loc[index].to_dict()
try: try:
main_case = dict(self.c.loc[index].to_dict()) main_case = dict(self.c_vc.loc[index].to_dict())
prex_list = list(filter(lambda x: x, [value.strip() for value in (main_case['PreExecCase'] or '').replace(',', "\n").split("\n")])) prex_list = list(filter(lambda x: x, [value.strip() for value in (main_case['PreExecCase'] or '').replace(',', "\n").split("\n")]))
for case_id in prex_list: for case_id in prex_list:
if self._match_prex_rule(main_case['PreExecRule']) == 10 and case_id in self.pre_executed_list: if self._match_prex_rule(main_case['PreExecRule']) == 10 and case_id in self.pre_executed_list:
continue continue
data = self.c.where((self.c['CaseId'] == case_id), inplace=False).dropna(how='all').reset_index(drop=True, inplace=False).loc[0].to_dict() data = self.c_vc.where((self.c_vc['CaseId'] == case_id), inplace=False).dropna(how='all').reset_index(drop=True, inplace=False).loc[0].to_dict()
if not self._match_case_type(data['Flag']) == 20:
raise Exception('A non-Pre-exec case was called.')
if data['PreExecCase'] or data['PreExecRule']: if data['PreExecCase'] or data['PreExecRule']:
raise Exception('Pre-exec case cannot be nested.') raise Exception('Pre-exec case cannot be nested.')
if data['DataFileSet']: if data['DataFileSet']:
raise Exception('Pre-exec case cannot use data files set.') raise Exception('Pre-exec case cannot use data files set.')
self.pre_executed_list.append(case_id) self.pre_executed_list.append(case_id)
self._test_unit(data=data) self._test_unit(func_name=func_name, data=data)
self._test_unit(data=main_case, main=True) # self.__getattribute__(func_name)(data=data)
self._test_unit(func_name=func_name, data=main_case, main=True)
# self.__getattribute__(func_name)(data=main_case, main=True)
self._l and self._l.clear() self._l and self._l.clear()
write_test_result(self.c_excel_object, main_case_object_cell, 'Passed') write_test_result(main_case_object_cell, 'Passed')
except AssertionError: except AssertionError:
self._l and self._l.clear() self._l and self._l.clear()
write_test_result(self.c_excel_object, main_case_object_cell, 'Failed') write_test_result(main_case_object_cell, 'Failed')
raise raise
except Exception: except Exception:
self._l and self._l.clear() self._l and self._l.clear()
write_test_result(self.c_excel_object, main_case_object_cell, 'Errors') write_test_result(main_case_object_cell, 'Errors')
raise raise
def main(self): def main(self):
from pandas import read_csv
class ClassTestCase(unittest.TestCase): class ClassTestCase(unittest.TestCase):
pass pass
ClassTestCase.__qualname__ = self.case ClassTestCase.__qualname__ = self.case
ClassTestCase._test_ = self.test ClassTestCase._test_ = self.test
serial = 0
for i in range(len(self.c)): from pandas import read_csv
data = self.c.loc[i].to_dict()
if not (self._match_case_type(data['Flag']) == 10 and self._match_leve_run(data['CaseLevel'])): continue while 1:
serial += 1 if self.organize_mode == 20:
filename = data['DataFileSet'] or '' levels_list = self.leve_list
if filename: loop = len(levels_list)
data_file = read_csv(os.path.abspath(os.path.join(self.dirs, './%s' % filename)), keep_default_na=False)
data_file_rows = len(data_file)
data_file_flag = 1
else: else:
data_file = None levels_list = None
data_file_rows = 0 loop = 1
data_file_flag = 0 break
j = 0
while j < data_file_rows or data_file_flag == 0:
match data_file_flag:
case 1:
vars_data = data_file.loc[j].to_dict()
func_docs = str(data['CaseTitle'] or '') + '_' + '_'.join([str(v) for k, v in vars_data.items()])[:32]
func_name = 'test%s_%s_%s' % (str('%04d' % serial), str(data['CaseId']), str('%04d' % (j + 1)))
once_do_break = 0
case _:
vars_data = None
func_docs = str(data['CaseTitle'] or '')
func_name = 'test%s_%s' % (str('%04d' % serial), str(data['CaseId']))
once_do_break = 1
def func(self, index=i, update_global=None, update_locals=vars_data): serial = 0
return self._test_(index=index, update_global=update_global, update_locals=update_locals) for n in range(loop):
filter_module = self.c_vc['CaseModule'] == self.module_name if self.module_name else self.c_vc['CaseModule'] != '*'
if self.organize_mode == 20:
case = self.c_vc.where(filter_module & (self.c_vc['CaseLevel'] == levels_list[n]), inplace=False).dropna(how='all')
else:
case = self.c_vc.where(filter_module, inplace=False).dropna(how='all')
for i in range(len(case)):
data = case.iloc[i].to_dict()
if not (self._match_case_type(data['Flag']) == 10 and self._match_leve_run(data['CaseLevel'])): continue
serial += 1
filename = data['DataFileSet'] or ''
if filename:
data_file = read_csv(os.path.abspath(os.path.join(self.dirs, './%s' % filename)), keep_default_na=False)
data_file_rows = len(data_file)
data_file_flag = 1
else:
data_file = None
data_file_rows = 0
data_file_flag = 0
j = 0
while j < data_file_rows or data_file_flag == 0:
match data_file_flag:
case 1:
vars_data = data_file.loc[j].to_dict()
func_docs = str(data['CaseLevel'] or 'P?') + ':' + str(data['CaseTitle'] or '') + '_' + '_'.join([str(v) for k, v in vars_data.items()])[:32]
func_name = 'test%s_%s_%s' % (str('%04d' % serial), str(data['CaseId']), str('%04d' % (j + 1)))
once_do_break = 0
case _:
vars_data = None
func_docs = str(data['CaseLevel'] or 'P?') + ':' + str(data['CaseTitle'] or '')
func_name = 'test%s_%s' % (str('%04d' % serial), str(data['CaseId']))
once_do_break = 1
func.__doc__ = func_docs def func(self, index=data['index'], update_global=None, update_locals=vars_data, func_name=func_name):
type.__setattr__(ClassTestCase, func_name, func) return self._test_(index=index, update_global=update_global, update_locals=update_locals, func_name=func_name)
j += 1
if once_do_break: break
# if filename:
# data_file = read_csv(os.path.join(self.dirs, './%s' % filename), keep_default_na=False)
# for j in range(len(data_file)):
# vars_data = data_file.loc[j].to_dict()
#
# def func(self, index=i, update_global=None, update_locals=vars_data):
# return self._test_(index=index, update_global=update_global, update_locals=update_locals)
#
# func.__doc__ = (data['CaseTitle'] or '') + '_' + '_'.join([str(v) for k, v in vars_data.items()])[:32]
# type.__setattr__(ClassTestCase, 'test%s_%s_%s' % (str('%04d' % serial), str(data['CaseId']), str('%04d' % (j+1))), func)
# else:
# def func(self, index=i, update_global=None, update_locals=None):
# return self._test_(index=index, update_global=update_global, update_locals=update_locals)
#
# func.__doc__ = data['CaseTitle']
# type.__setattr__(ClassTestCase, 'test%s_%s' % (str('%04d' % serial), str(data['CaseId'])), func)
func.__doc__ = func_docs
type.__setattr__(ClassTestCase, func_name, func)
j += 1
if once_do_break: break
return ClassTestCase return ClassTestCase
if __name__ == '__main__': if __name__ == '__main__':
def read_runner_config(file: str):
import os
path = os.path.abspath(os.path.join(os.getcwd(), file))
return auto_decode(open(path, 'r', encoding='utf-8').read())
def abs_path(file: str):
if isinstance(file, str):
return os.path.abspath(os.path.join(os.getcwd(), file))
else:
return None
test_class = TestCase(
workbook='D:/Desktop/2.xlsx',
testcase='测试用例',
module='',
testlves='P0,P1',
organize_mode='BY_LEVEL',
write_test_result=True
).main()
test_suite = unittest.TestSuite() test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(testCaseClass=TestCase( test_suite.addTest(unittest.makeSuite(testCaseClass=test_class, prefix='test'))
workbook='D:/Desktop/接口自动化测试用例.xlsx', HTMLTestRunner(verbosity=5, log=os.path.abspath(os.path.join(os.path.dirname(__file__), 'D:/Desktop/2.log')), report=os.path.abspath(os.path.join(os.path.dirname(__file__), './1.html'))).run(test_suite)
testlves=None,
cases='测试用例', exit(0)
httpc='请求配置', argparser = ArgumentParser(
mysql='请求配置' description='API Test Runner',
).main(), prefix='test')) usage="\n" + os.path.basename(__file__) + ' [-c CONFIG]' + "\n" + os.path.basename(__file__) + ' [-w WORKBOOK] [-t TESTCASE] [-m MODULE] [-l LEVELS] [-o ORGANIZE] [-r WRITE_TEST_RESULT] [-v VERBOSITY] [-p REPORT] [-x LOG] [-i TITLE] [-d DESCRIPTION]'
HTMLTestRunner(verbosity=5, report=os.path.abspath(os.path.join(os.path.dirname(__file__), './1.html'))).run( )
test_suite) argparser.add_argument(
'-c',
required=bool(0), help='Load runner configuration file.',
choices=None, default=None, dest='config'
)
argparser.add_argument(
'-w',
required=bool(0), help='Excel workbook path.',
choices=None, default='./', dest='workbook'
)
argparser.add_argument(
'-t',
required=bool(0), help='Testcase worksheet name.',
choices=None, default='测试用例', dest='testcase'
)
argparser.add_argument(
'-m',
required=bool(0), help='You can specify a module name.',
choices=None, default=None, dest='module'
)
argparser.add_argument(
'-l',
required=bool(0), help='Case levels can be specified, use comma to separate, e.g. P0,P1,P2.',
choices=None, default=None, dest='levels'
)
argparser.add_argument(
'-o',
required=bool(0), help='Case organization mode, 1: by order, 2: by level, default: 1.',
choices=[1, 2], default=None, dest='organize', type=int
)
argparser.add_argument(
'-r',
required=bool(0), help='Enable writing test results to current worksheet, default: 0.',
choices=[bool(0), bool(1)], default=None, dest='write_test_result', type=bool
)
argparser.add_argument(
'-v',
required=bool(0), help='Level of report and log verbosity(0-5), default: 5.',
choices=[0, 1, 2, 3, 4, 5], default=5, dest='verbosity', type=int
)
argparser.add_argument(
'-p',
required=bool(0), help='Output path of report.',
choices=None, default=None, dest='report'
)
argparser.add_argument(
'-x',
required=bool(0), help='Output path of log.',
choices=None, default=None, dest='log'
)
argparser.add_argument(
'-i',
required=bool(0), help='Report title.',
choices=None, default=None, dest='title'
)
argparser.add_argument(
'-d',
required=bool(0), help='Report description.',
choices=None, default=None, dest='description'
)
args = argparser.parse_args()
print(args)
if args.config:
config = read_runner_config(args.config)
else:
config = {
'verbosity': args.verbosity, 'report': args.report, 'log': args.log, 'title': args.title, 'description': args.description,
'suite': [{"workbook": args.workbook, "testcase": args.testcase, "module": args.module, "levels": args.levels, "organize": args.organize, "result": args.write_test_result}]
}
test_suite = unittest.TestSuite()
for obj in config['suite']:
test_suite.addTest(unittest.makeSuite(
testCaseClass=TestCase(workbook=obj['workbook'], testcase=obj['testcase'], module=obj['module'], testlves=obj['levels'], organize_mode=obj['organize'], write_test_result=obj['result']).main(), prefix='test'
))
HTMLTestRunner(verbosity=config['verbosity'], report=abs_path(config['report']), log=abs_path(config['log']), title=config['title'], description=config['description']).run(test_suite)

View File

@ -0,0 +1 @@
{"verbosity": 5, "report": null, "log": null, "title": null, "description": null , "suite": [{"workbook": "D:/Desktop/接口自动化测试用例.xlsx", "testcase": "测试用例", "module": null, "levels": "P0,P1,P2,P3", "organize": 1, "result": true}]}

Binary file not shown.

View File

@ -1,194 +0,0 @@
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
X7czZGo45GWe2Wge8lP6iCxKNKf3FclEfzF3ASalaLc23IQ9DTS7gnp6J+IY3Rxl0KVJ4XNYpkO7
Hlsd4/3LmVZxR+ZRUuHDV6K0++QHUEsDBBQAAAAIAAZX4VS5NC0njAAAAO4AAAAjAAAAeGwvZHJh
d2luZ3MvX3JlbHMvZHJhd2luZzEueG1sLnJlbHONzzsKwzAQBNCriD2A106RIliu0rgNvsAirywT
64O0Aef2ESSFAylSzgw8mP7GG8kaQ3FrKmr3WyganEi6IBbj2FNpYuJQFxuzJ6kxL5jI3GlhPLXt
GfPRgKE/mmp6Jv5HjNauhq/RPDwH+QGjcZQF1ER5YdGA+/auPkvXVBHUOGvI49yBwqHHr3vDC1BL
AwQUAAAACAAGV+FUJ7Xw444AAAD0AAAAIwAAAHhsL3dvcmtzaGVldHMvX3JlbHMvc2hlZXQxLnht
bC5yZWxzjc87CsMwEATQq4g9gNdOkSLIrtK4Db7AIq8+xPogycS5fdwkOJAi5czAg5E3Xqi6GIp1
qYjNL6H0YGtNF8SiLHsqTUwc9kXH7KnuMRtMpO5kGE9te8Z8NGCQR1NMz8T/iFFrp/ga1eo51B8w
zpkeLhgQE2XDtQfclnf5Wbtmd0GMcw95nDsQOEj8Ojm8AFBLAwQUAAAACAAGV+FU5cuAVrcBAADq
AwAAGAAAAHhsL3dvcmtzaGVldHMvc2hlZXQyLnhtbHVT227bMAz9FUEfENlp0q2FbSBxUqxABxQN
tj4rMR0LlS1VYuJtXz9JqW9A8iSS55A6FKmkVebDVgBI/tSysSmtEPUjY/ZQQc3tTGloHFIqU3N0
rjkyqw3wIiTVks2j6J7VXDQ0S0Ls1WSJOqEUDbwaYk91zc3fNUjVpjSmXeBNHCsMAZYlmh9hB/hL
uwTnsr5OIWporFANMVCmdBU/vsSXlED5LaC1I5sg3+9AwgGhCLV9d3ulPjz47EKRFxkIviZ3xxly
kNKVXjppn90t2yUdhPjksd3d+BTexEnecwu5ku+iwCql3ykpoOQniUPsoY+9qfYHfPW+mC2GXjYc
eZYY1RLjpWfJwRsr14Sj+sc9Z3HCzk7I4Qtbj7H5FMvH2N0U24yxxRTbjrFljzGnqxc378XNA1k0
ftY7NC4u3ONgtkoYuiTvDGpvkddXyPktcn6FvLlF3lwhb2+Rt1PypOOHMKZuNtHEi0ebcpmh3+af
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

Binary file not shown.

View File

@ -1,16 +1,6 @@
import pandas as pd try:
# from Base.Class.Yaml import * assert 1 == 2
# from pandas import read_csv except AssertionError:
# df = read_csv("D:/Desktop/新建 XLSX 工作表.csv", keep_default_na=False) print('断言错误')
# print(df.loc[2].to_dict()) except Exception:
from pandas.core.frame import DataFrame print('出错了')
from Base.Class.Database import *
conn = MySQL().connect(host='sdm821105491.my3w.com', port=3306, user='sdm821105491', password='1234ABCDabcd', database='sdm821105491_db', charset='utf8')
conn.execute('select gid,title,date,content from %s;' % 'emlog_blog')
data = conn.fetchall()
# print(data)
print(conn.brief())

View File

@ -1,16 +1,34 @@
# from Base.Class.Excel import * # from argparse import ArgumentParser
# from Business.Class.ExcelUtils import * # def func():
# print('哈哈哈哈')
# #
# # excel = Excel().open(filename='D:/Desktop/接口自动化测试用例.xlsx', sheet='测试用例') # if __name__ == "__main__":
# # data = excel.cellGetView(area='7A:7AH', ocell=True) # argparser = ArgumentParser(description='API Test Runner')
# # print(data) # argparser.add_argument(
# # # data[0][0] = '11111' # '--',
# # excel.save() # required=bool(1), help='',
# a = read_view_dict(filename='D:/Desktop/接口自动化测试用例.xlsx', sheet='测试用例', area='A7:AH7', object_cell=True) # choices=None, default=''
# print(a[0]['col0'].value) # )
# a[0]['col0'].value = 'Passed' #
# print(a[0]['col0'].value) # results = argparser.parse_args()
# from Base.Class.Time import * # print(results)
# print(time_strftime(fmt='%Y-%m-%d %H:%M:%S')) # from Business.Class.JsonOrYaml import *
for k, v in [[1,2],[3,4]]: #
print(k,v) #
# def read_runner_config(file: str):
# import os
# path = os.path.abspath(os.path.join(os.path.dirname(__file__), file))
# if os.path.exists(path):
# return auto_decode(open(path, 'r', encoding='utf-8').read())
# else:
# return False
#
#
# print(read_runner_config('D:/Project/AutoFramework/Runner/API/api_runner.json'))
# import os
# print(os.path.abspath(os.path.join(os.getcwd(), './1/2/log.txt')))
import os
print(os.path.abspath(os.path.join(os.path.dirname(__file__), 'D:/Desktop/2.log')))
a = 2 == 2 if os else 1 != 2

View File

@ -1,703 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>自动化测试报告</title>
<meta name="generator" content="HTMLTestRunner"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1.0"/>
<link rel="stylesheet" href="https://www.fanscloud.net/res/bootstrap/3.0.3/css/bootstrap.min.css">
<script src="https://www.fanscloud.net/res/jquery/2.0.0/jquery.min.js"></script>
<script src="https://www.fanscloud.net/res/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<script src="https://img.hcharts.cn/highcharts/highcharts.js"></script>
<script src="https://img.hcharts.cn/highcharts/modules/exporting.js"></script>
<style type="text/css" media="screen">
body { font-family: Microsoft YaHei;padding: 20px; font-size: 100%; }
table { font-size: 100%; }
.table tbody tr td{
vertical-align: middle;
}
/* -- header ---------------------------------------------------------------------- */
.header .description, .attribute {
clear: both;
}
/* --- 失败和错误合集样式 -- Gelomen --- */
.failCollection, .errorCollection {
width: auto;
float: left;
}
#failedCaseOl li {
color: red
}
#errorsCaseOl li {
color: orange
}
/* --- 打开截图特效样式 -- Gelomen --- */
.data-img{
cursor:pointer
}
.pic_looper{
width:100%;
height:100%;
position: fixed;
left: 0;
top:0;
opacity: 0.6;
background: #000;
display: none;
z-index: 100;
}
.pic_show{
width:100%;
position:fixed;
left:0;
top:0;
right:0;
bottom:0;
margin:auto;
text-align: center;
display: none;
z-index: 100;
}
.pic_box{
padding:10px;
width:90%;
height:90%;
margin:40px auto;
text-align: center;
overflow: hidden;
}
.pic_box img{
max-width: 100%;
max-height: 100%;
width: auto;
height: auto;
-moz-box-shadow: 0px 0px 20px 0px #000;
-webkit-box-shadow: 0px 0px 20px 0px #000;
box-shadow: 0px 0px 20px 0px #000;
}
/* --- 饼状图样式 */
#container {
max-width: 100%;
width: 450px;
height: 350px;
float: left;
}
#container_extend {
max-width: 100%;
width: 550px;
height: 400px;
float: left;
}
/* -- report ------------------------------------------------------------------------ */
#total_row { font-weight: bold; }
.passedCase { color: #3FB83F; font-family: Menlo,Monaco,Consolas,"Courier New",monospace; font-size: 14px; font-weight: bold; }
.failedCase { color: #D9433F; font-family: Menlo,Monaco,Consolas,"Courier New",monospace; font-size: 14px; font-weight: bold; }
.errorsCase { color: #F0A02F; font-family: Menlo,Monaco,Consolas,"Courier New",monospace; font-size: 14px; font-weight: bold; }
.hiddenRow { display: none; }
.testcase { margin-left: 1em; word-break: break-all; white-space: pre-wrap; }
.screenshot:link { text-decoration: none;color: deeppink; }
.screenshot:visited { text-decoration: none;color: deeppink; }
.screenshot:hover { text-decoration: none;color: darkcyan; }
.screenshot:active { text-decoration: none;color: deeppink; }
</style>
</head>
<body >
<script language="javascript" type="text/javascript">
$(function(){
// 修改 失败 和 错误 用例里对应按钮的颜色ClassName为动态加载 -- Gelomen
$("button").each(function () {
var text = $(this).text();
if(text == "失败"){
$(this).addClass("btn-danger")
}else if(text == "错误") {
$(this).addClass("btn-warning")
}else if(text == "通过") {
$(this).addClass("btn-success")
}
});
// 给失败和错误合集加样式 -- Gelomen
var p_attribute = $("p.attribute");
p_attribute.eq(4).addClass("failCollection");
p_attribute.eq(5).addClass("errorCollection");
// 打开截图,放大,点击任何位置可以关闭图片
$(".screenshot").click(function(){
var img = $(this).attr("img");
$('.pic_show img').attr('src', img);
$('.pic_looper').fadeIn(200);
$('.pic_show').fadeIn(200);
var browserHeight = $(window).height();
var pic_boxHeight = $(".pic_box").height();
var top = (browserHeight - pic_boxHeight)/2;
$('.pic_box').css("margin-top", top + "px")
});
$('.pic_looper, .pic_show').click(function(){
$('.pic_looper').fadeOut(200);
$('.pic_show').fadeOut(200)
});
var resize_action = function(){
// 改变窗口大小时,自动改变图表边距
var browserWidth = $(window).width();
var margin_left = browserWidth - 360 - 450 - 550 - 40;
if(margin_left <= 0){
$("#container").css("width", "100%");
$("#container_extend").css("width", "100%");
$("#testinfo").css("width", "100%");
$("#container").css("margin-left", "0px");
}else {
$("#container").css("width", "450px");
$("#container_extend").css("width", "550px");
$("#testinfo").css("width", "25%");
$("#container").css("margin-left", (margin_left - 1) + "px");
}
}
resize_action();
$(window).resize(function(){
// 改变窗口大小时,自动改变图片与顶部的距离
var browserHeight = $(window).height();
var pic_boxHeight = $(".pic_box").height();
var top = (browserHeight - pic_boxHeight)/2;
$('.pic_box').css("margin-top", top + "px");
resize_action();
});
// 超过浏览器高度时,回到顶部按钮出现
$(window).scroll(function(){
var browserHeight = $(window).height();
var top = $(window).scrollTop();
if(top >= browserHeight){
$("#toTop").css("display", "block")
}else {
$("#toTop").css("display", "none")
}
})
// 增加回到顶部过程动画
$("#toTop").click(function() {
$("html,body").animate({"scrollTop":0}, 500)
})
// 增加条形图
$('#container_extend').highcharts({
chart: {
type: 'bar'
},
credits: {
enabled: false
},
navigation: {
buttonOptions: {
enabled: false
}
},
title: {
text: '用例集合情况'
},
xAxis: {
categories: ["TestDemo", "测试用例"]
},
yAxis: {
min: 0,
title: {
text: '用例数量'
},
reversedStacks: false
},
legend: {
reversed: false
},
plotOptions: {
series: {
stacking: 'normal'
}
},
series: [{
name: '通过',
color: '#64bb64',
data: [1, 0]
}, {
name: '失败',
color: '#f16d7e',
data: [1, 0]
}, {
name: '错误',
color: '#fdc68c',
data: [3, 1]
}]
})
// 增加饼状图
$('#container').highcharts({
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
spacing : [0, 0, 0, 0]
},
credits: {
enabled: false
},
navigation: {
buttonOptions: {
enabled: false
}
},
title: {
floating: true,
text: '测试结果占比'
},
tooltip: {
pointFormat: '<text style="font-size:10px">{series.name}: {point.percentage:.1f}%</text>'
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
colors: ['#64bb64', '#f16d7e', '#fdc68c'],
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.percentage:.1f} %',
style: {
color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black'
}
},
point: {
events: {
mouseOver: function(e) { // 鼠标滑过时动态更新标题
chart.setTitle({
text: e.target.name+ '\t'+ e.target.y + ' 个'
});
}
}
}
}
},
series: [{
type: 'pie',
innerSize: '80%',
name: '比例',
data: [
['通过', 1], ['失败', 1], ['错误', 4]
]
}]
}, function(c) {
// 环形圆心
var centerY = c.series[0].center[1],
titleHeight = parseInt(c.title.styles.fontSize);
c.setTitle({
x:0,
y:centerY + titleHeight/2
});
chart = c;
});
// 查看 失败 和 错误 合集链接文字切换 -- Gelomen
$(".showDetail").click(function () {
if($(this).html() == "点击查看"){
$(this).html("点击收起")
}else {
$(this).html("点击查看")
}
})
});
output_list = Array();
/*level 调整增加只显示通过用例的分类 --Findyou / 修复筛选显示bug --Gelomen
0:Summary //all hiddenRow
1:Failed //pt&et hiddenRow, ft none
2:Passed //pt none, ft&et hiddenRow
3:Errors //pt&ft hiddenRow, et none
4:All //all none
*/
function showCase(level) {
trs = document.getElementsByTagName("tr");
for (var i = 0; i < trs.length; i++) {
tr = trs[i];
id = tr.id;
if (id.substr(0,2) == 'ft') {
if (level == 2 || level == 0 || level == 3) {
tr.className = 'hiddenRow';
}
else {
tr.className = '';
// 切换筛选时只显示预览 -- Gelomen
// 失败
$("div[id^='div_ft']").attr("class", "collapse");
$("div[id^='div_et']").attr("class", "collapse");
$("div[id^='div_pt']").attr("class", "collapse");
}
}
if (id.substr(0,2) == 'pt') {
if (level == 1 || level == 0 || level == 3) {
tr.className = 'hiddenRow';
}
else {
tr.className = '';
// 切换筛选时只显示预览 -- Gelomen
// 通过
$("div[id^='div_ft']").attr("class", "collapse");
$("div[id^='div_et']").attr("class", "collapse");
$("div[id^='div_pt']").attr("class", "collapse");
}
}
if (id.substr(0,2) == 'et') {
if (level == 1 || level == 0 || level == 2) {
tr.className = 'hiddenRow';
}
else {
tr.className = '';
// 切换筛选时只显示预览 -- Gelomen
// 错误
$("div[id^='div_ft']").attr("class", "collapse");
$("div[id^='div_et']").attr("class", "collapse");
$("div[id^='div_pt']").attr("class", "collapse");
}
}
}
//加入详细切换文字变化
detail_class=document.getElementsByClassName('detail');
if (level == 4) {
for (var i = 0; i < detail_class.length; i++){
detail_class[i].innerHTML="收起"
}
}
else{
for (var i = 0; i < detail_class.length; i++){
detail_class[i].innerHTML="详细"
}
}
}
function showClassDetail(cid, count) {
var id_list = Array(count);
var toHide = 1;
for (var i = 0; i < count; i++) {
//ID修改.为_
tid0 = 't' + cid.substr(1) + '_' + (i+1);
tid = 'f' + tid0;
tr = document.getElementById(tid);
if (!tr) {
tid = 'p' + tid0;
tr = document.getElementById(tid);
if (!tr) {
tid = 'e' + tid0;
tr = document.getElementById(tid);
}
}
id_list[i] = tid;
if (tr.className) {
toHide = 0;
}
}
for (var i = 0; i < count; i++) {
tid = id_list[i];
//修改点击无法收起的BUG加入【详细】切换文字变化 --Findyou
if (toHide) {
document.getElementById(tid).className = 'hiddenRow';
document.getElementById(cid).innerText = "详细"
}
else {
document.getElementById(tid).className = '';
document.getElementById(cid).innerText = "收起"
}
}
}
function html_escape(s) {
s = s.replace(/&/g,'&amp;');
s = s.replace(/</g,'&lt;');
s = s.replace(/>/g,'&gt;');
return s;
}
</script>
<div class='pic_looper'></div>
<div class='pic_show'>
<div class='pic_box'>
<img src=''/>
</div>
</div>
<div class='header'>
<div id="testinfo" style="max-width: 360px; width: auto; float: left;">
<h1 style="margin: 5px 0px 10px 0px; font-family: Microsoft YaHei;">自动化测试报告</h1>
<p class='attribute'><strong>开始时间 : </strong> 2022-07-28 00:36:41</p>
<p class='attribute'><strong>合计耗时 : </strong> 00:00:02</p>
<p class='attribute'><strong>测试结果 : </strong> 总共 6通过 1失败 1错误 4通过率 16.67%</p>
<p class='attribute'><strong>失败用例 : </strong> <a class='showDetail' data-toggle='collapse' href='#failedCaseOl' style='text-decoration: none;'>点击查看</a><ol id='failedCaseOl' class='collapse' style='float: left; font-family: Menlo,Monaco,Consolas,monospace;'><li>test_case.TestDemo.test_two22</li></ol></p>
<p class='attribute'><strong>错误用例 : </strong> <a class='showDetail' data-toggle='collapse' href='#errorsCaseOl' style='text-decoration: none;'>点击查看</a><ol id='errorsCaseOl' class='collapse' style='float: left; font-family: Menlo,Monaco,Consolas,monospace;'><li>test_case.TestDemo.test_tre333</li><li>test_case.TestDemo.test_fou444</li><li>main.测试用例.test_demo1</li><li>test_case.TestDemo.test_0123456789ABC</li></ol></p>
<p class='description'>描述</p>
</div>
<div id="container"></div>
<div id="container_extend"></div>
</div>
<div style="width: auto; clear: both;">
<p id='show_detail_line'>
<a class="btn btn-primary" href='javascript:showCase(0)'>概要 16.67%</a>
<a class="btn btn-success" href='javascript:showCase(2)'>通过 1</a>
<a class="btn btn-danger" href='javascript:showCase(1)'>失败 1</a>
<a class="btn btn-warning" href='javascript:showCase(3)'>错误 4</a>
<a class="btn btn-info" href='javascript:showCase(4)'>全部 6</a>
</p>
</div>
<table id='result_table' class="table table-condensed table-bordered table-hover">
<colgroup>
<col align='left' style="width: 300px;"/>
<col align='right' style="width: 285px;"/>
<col align='right' />
<col align='right' />
<col align='right' />
<col align='right' />
<col align='right' />
<col align='right' style="width: 120px;"/>
</colgroup>
<tr id='header_row' class="text-center success" style="font-weight: bold;font-size: 14px;">
<td>测试用例</td>
<td>说明</td>
<td>总计</td>
<td>通过</td>
<td>失败</td>
<td>错误</td>
<td>耗时</td>
<td>详细</td>
</tr>
<tr class='errorClass warning'>
<td>TestDemo</td>
<td></td>
<td class="text-center">5</td>
<td class="text-center">1</td>
<td class="text-center">1</td>
<td class="text-center">3</td>
<td class="text-center">2.091秒</td>
<td class="text-center"><a href="javascript:showClassDetail('c1',5)" class="detail" id='c1'>查看全部</a></td>
</tr>
<tr id='pt1_1' class='hiddenRow'>
<td class='passedCase' style="vertical-align: middle"><div class='testcase'>test_one1</div></td>
<td style="vertical-align: middle"></td>
<td colspan='5' align='center'>
<button id='btn_pt1_1' type="button" class="btn btn-xs" data-toggle="collapse" data-target='#div_pt1_1'>通过</button>
<div id='div_pt1_1' class="collapse in">
<pre style="text-align:left;font-size:12px;color:#e52000">pt1_1:
STDOUT
STDERR
</pre>
</div>
</td>
<td class='passedCase' style="vertical-align: middle"></td>
</tr>
<tr id='ft1_2' class='none'>
<td class='failedCase' style="vertical-align: middle"><div class='testcase'>test_two22</div></td>
<td style="vertical-align: middle"></td>
<td colspan='5' align='center'>
<button id='btn_ft1_2' type="button" class="btn btn-xs" data-toggle="collapse" data-target='#div_ft1_2'>失败</button>
<div id='div_ft1_2' class="collapse in">
<pre style="text-align:left;font-size:12px;color:#e52000">ft1_2:
2022-07-28 00:36:41,298 - E: 错误啦!!!
Traceback (most recent call last):
File "D:\Project\AutoFramework\test_case.py", line 31, in test_two22
assert 'A' in 'Hello!', '断言失败'
AssertionError: 断言失败
</pre>
</div>
</td>
<td class='failedCase' style="vertical-align: middle"></td>
</tr>
<tr id='et1_3' class='none'>
<td class='errorsCase' style="vertical-align: middle"><div class='testcase'>test_tre333</div></td>
<td style="vertical-align: middle"></td>
<td colspan='5' align='center'>
<button id='btn_et1_3' type="button" class="btn btn-xs" data-toggle="collapse" data-target='#div_et1_3,#div_et1_3_screenshot'>错误</button>
<div id='div_et1_3' class="collapse in">
<pre style="text-align:left;font-size:12px;color:#e52000">et1_3:
Traceback (most recent call last):
File "D:\Project\AutoFramework\test_case.py", line 35, in test_tre333
a(1)
NameError: name 'a' is not defined
</pre>
</div>
</td>
<td class="text-center" style="vertical-align: middle">
<div style='width:auto;height:auto;border:1px solid #b5b5b5;color:#202020font-weight:bold;text-align:center'>截图信息</div>
<div id='div_et1_3_screenshot' style="text-align: left" class="collapse in">
<a style="font-family:Consolas,monospace;color:#e52000;text-decoration:underline;" class="screenshot" href="javascript:void(0)" img="./01.png">01.png</a></br>
</div>
</td>
</tr>
<tr id='et1_4' class='none'>
<td class='errorsCase' style="vertical-align: middle"><div class='testcase'>test_fou444</div></td>
<td style="vertical-align: middle"></td>
<td colspan='5' align='center'>
<button id='btn_et1_4' type="button" class="btn btn-xs" data-toggle="collapse" data-target='#div_et1_4,#div_et1_4_screenshot'>错误</button>
<div id='div_et1_4' class="collapse in">
<pre style="text-align:left;font-size:12px;color:#e52000">et1_4:
Traceback (most recent call last):
File "D:\Project\AutoFramework\test_case.py", line 43, in test_fou444
raise Exception('Errors')
Exception: Errors
</pre>
</div>
</td>
<td class="text-center" style="vertical-align: middle">
<div style='width:auto;height:auto;border:1px solid #b5b5b5;color:#202020font-weight:bold;text-align:center'>截图信息</div>
<div id='div_et1_4_screenshot' style="text-align: left" class="collapse in">
<a style="font-family:Consolas,monospace;color:#e52000;text-decoration:underline;" class="screenshot" href="javascript:void(0)" img="./01.png">01.png</a></br><a style="font-family:Consolas,monospace;color:#e52000;text-decoration:underline;" class="screenshot" href="javascript:void(0)" img="./2022072523501942.png">2022072523501942.png</a></br><a style="font-family:Consolas,monospace;color:#e52000;text-decoration:underline;" class="screenshot" href="javascript:void(0)" img="./2022072523501235.png">2022072523501235.png</a></br>
</div>
</td>
</tr>
<tr id='et1_5' class='none'>
<td class='errorsCase' style="vertical-align: middle"><div class='testcase'>test_0123456789ABC</div></td>
<td style="vertical-align: middle"></td>
<td colspan='5' align='center'>
<button id='btn_et1_5' type="button" class="btn btn-xs" data-toggle="collapse" data-target='#div_et1_5'>错误</button>
<div id='div_et1_5' class="collapse in">
<pre style="text-align:left;font-size:12px;color:#e52000">et1_5:
Traceback (most recent call last):
File "D:\Project\AutoFramework\venv\lib\site-packages\urllib3\connection.py", line 174, in _new_conn
conn = connection.create_connection(
File "D:\Project\AutoFramework\venv\lib\site-packages\urllib3\util\connection.py", line 95, in create_connection
raise err
File "D:\Project\AutoFramework\venv\lib\site-packages\urllib3\util\connection.py", line 85, in create_connection
sock.connect(sa)
ConnectionRefusedError: [WinError 10061] 由于目标计算机积极拒绝,无法连接。
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "D:\Project\AutoFramework\venv\lib\site-packages\urllib3\connectionpool.py", line 703, in urlopen
httplib_response = self._make_request(
File "D:\Project\AutoFramework\venv\lib\site-packages\urllib3\connectionpool.py", line 398, in _make_request
conn.request(method, url, **httplib_request_kw)
File "D:\Project\AutoFramework\venv\lib\site-packages\urllib3\connection.py", line 239, in request
super(HTTPConnection, self).request(method, url, body=body, headers=headers)
File "C:\Program Files\Python310\lib\http\client.py", line 1282, in request
self._send_request(method, url, body, headers, encode_chunked)
File "C:\Program Files\Python310\lib\http\client.py", line 1328, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "C:\Program Files\Python310\lib\http\client.py", line 1277, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "C:\Program Files\Python310\lib\http\client.py", line 1037, in _send_output
self.send(msg)
File "C:\Program Files\Python310\lib\http\client.py", line 975, in send
self.connect()
File "D:\Project\AutoFramework\venv\lib\site-packages\urllib3\connection.py", line 205, in connect
conn = self._new_conn()
File "D:\Project\AutoFramework\venv\lib\site-packages\urllib3\connection.py", line 186, in _new_conn
raise NewConnectionError(
urllib3.exceptions.NewConnectionError: &lt;urllib3.connection.HTTPConnection object at 0x00000272B0967280&gt;: Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接。
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "D:\Project\AutoFramework\venv\lib\site-packages\requests\adapters.py", line 489, in send
resp = conn.urlopen(
File "D:\Project\AutoFramework\venv\lib\site-packages\urllib3\connectionpool.py", line 785, in urlopen
retries = retries.increment(
File "D:\Project\AutoFramework\venv\lib\site-packages\urllib3\util\retry.py", line 592, in increment
raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='127.0.0.1', port=8888): Max retries exceeded with url: http://more-md.fanscloud.net/iplookup (Caused by ProxyError('Cannot connect to proxy.', NewConnectionError('&lt;urllib3.connection.HTTPConnection object at 0x00000272B0967280&gt;: Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接。')))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "D:\Project\AutoFramework\test_case.py", line 53, in test_0123456789ABC
Request().http('GET',"http://more-md.fanscloud.net/iplookup", proxy="http://127.0.0.1:8888")
File "D:\Project\AutoFramework\Base\Class\Http.py", line 75, in http
self.__response_object__ = self.__http__.request(
File "D:\Project\AutoFramework\venv\lib\site-packages\requests\api.py", line 59, in request
return session.request(method=method, url=url, **kwargs)
File "D:\Project\AutoFramework\venv\lib\site-packages\requests\sessions.py", line 587, in request
resp = self.send(prep, **send_kwargs)
File "D:\Project\AutoFramework\venv\lib\site-packages\requests\sessions.py", line 701, in send
r = adapter.send(request, **kwargs)
File "D:\Project\AutoFramework\venv\lib\site-packages\requests\adapters.py", line 559, in send
raise ProxyError(e, request=request)
requests.exceptions.ProxyError: HTTPConnectionPool(host='127.0.0.1', port=8888): Max retries exceeded with url: http://more-md.fanscloud.net/iplookup (Caused by ProxyError('Cannot connect to proxy.', NewConnectionError('&lt;urllib3.connection.HTTPConnection object at 0x00000272B0967280&gt;: Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接。')))
</pre>
</div>
</td>
<td class='errorsCase' style="vertical-align: middle"></td>
</tr>
<tr class='errorClass warning'>
<td>测试用例</td>
<td>Doc</td>
<td class="text-center">1</td>
<td class="text-center">0</td>
<td class="text-center">0</td>
<td class="text-center">1</td>
<td class="text-center">0.000秒</td>
<td class="text-center"><a href="javascript:showClassDetail('c2',1)" class="detail" id='c2'>查看全部</a></td>
</tr>
<tr id='et2_1' class='none'>
<td class='errorsCase' style="vertical-align: middle"><div class='testcase'>test_demo1</div></td>
<td style="vertical-align: middle">这是一条测试用例;这是一条测试用例;这是一条测试用例;这是一条测试用例;这是一条测试用例;这是一条测试用例;这是一条测试用例;这是一条测试用例;这是一条测试用例;</td>
<td colspan='5' align='center'>
<button id='btn_et2_1' type="button" class="btn btn-xs" data-toggle="collapse" data-target='#div_et2_1'>错误</button>
<div id='div_et2_1' class="collapse in">
<pre style="text-align:left;font-size:12px;color:#e52000">et2_1:
Traceback (most recent call last):
File "", line 1, in foo
NameError: name 'a' is not defined
</pre>
</div>
</td>
<td class='errorsCase' style="vertical-align: middle"></td>
</tr>
<tr id='total_row' class="text-center active">
<td colspan='2'>总计</td>
<td>6</td>
<td>1</td>
<td>1</td>
<td>4</td>
<td>2.091秒</td>
<td>通过16.67%</td>
</tr>
</table>
<div id='footer'>&nbsp;</div>
<div id="toTop" style="position:fixed;right:50px; bottom:30px; width:20px; height:20px;cursor:pointer; display: none">
<a>
<span class="glyphicon glyphicon-eject" style = "font-size:28px;color:#b0b0b0" aria-hidden="true">
</span>
</a>
</div>
<div style='width: auto; height: auto; border: 1px solid #e3e3e3; text-align: center; color: #505050; padding: 4px 0px 4px 0px;'>
<img src='' style='width: auto; height: auto; max-height: 40px;'>
<a href='#' style='font-size: 14px; color: #505050; text-align: center|bottom;'></a>
</div>
</body>
</html>