From 15b21f25a54ae55b68e713717dddc0bc44645118 Mon Sep 17 00:00:00 2001 From: zhaoyafan Date: Fri, 12 Aug 2022 11:21:21 +0800 Subject: [PATCH] At Aug 12 A. --- Runner/API/1.html | 131 ++++++++++----- Runner/API/DefaultRunner.py | 325 +++++++++++++++++++++--------------- main4.py | 12 +- main5.py | 11 ++ 4 files changed, 293 insertions(+), 186 deletions(-) create mode 100644 main5.py diff --git a/Runner/API/1.html b/Runner/API/1.html index 6d00061..8166583 100644 --- a/Runner/API/1.html +++ b/Runner/API/1.html @@ -229,7 +229,7 @@ series: [{ name: '通过', color: '#64bb64', - data: [6] + data: [5] }, { name: '失败', color: '#f16d7e', @@ -237,7 +237,7 @@ }, { name: '错误', color: '#fdc68c', - data: [0] + data: [1] }] }) // 增加饼状图 @@ -291,7 +291,7 @@ innerSize: '80%', name: '比例', data: [ - ['通过', 6], ['失败', 0], ['错误', 0] + ['通过', 5], ['失败', 0], ['错误', 1] ] }] }, function(c) { @@ -438,15 +438,15 @@ function html_escape(s) {

测试报告

-

开始时间 : 2022-08-10 18:32:08

+

开始时间 : 2022-08-12 11:13:49

-

合计耗时 : 00:00:01

+

合计耗时 : 00:00:00

-

测试结果 : 总共 6,通过 6,失败 0,错误 0,通过率 100.00%

+

测试结果 : 总共 6,通过 5,失败 0,错误 1,通过率 83.33%

失败用例 :

-

错误用例 :

+

错误用例 : 点击查看

  1. main.测试用例.test0003_None

@@ -457,10 +457,10 @@ function html_escape(s) {

- 概要 100.00% - 通过 6 + 概要 83.33% + 通过 5 失败 0 - 错误 0 + 错误 1 全部 6

@@ -486,74 +486,121 @@ function html_escape(s) { 详细 - + 测试用例 6 - 6 + 5 0 - 0 - 1.154秒 + 1 + 0.720秒 查看全部
test0001_None
- - - - - - -
test0002_None
- - - - - - -
test0003_None
- -
-
pt1_3:
-{'var8': 'Windows、Linux修改连接数限制'}
+        
+        
+
pt1_1:
+{}
 
+ +
test0002_None
+ + + +
+
pt1_2:
+{}
+
+
+ + + + + +
test0003_None
+ + + +
+
et1_3:
+Traceback (most recent call last):
+  File "D:\Project\AutoFramework\Runner\API\DefaultRunner.py", line 579, in foo
+    return self._test_(index)
+  File "D:\Project\AutoFramework\Runner\API\DefaultRunner.py", line 564, in test
+    self.test_unit(data=main_case)
+  File "D:\Project\AutoFramework\Runner\API\DefaultRunner.py", line 500, in test_unit
+    self._assert([
+  File "D:\Project\AutoFramework\Runner\API\DefaultRunner.py", line 437, in _assert
+    assert_parm = [eval(form[0]), eval(form[1])]
+  File "<string>", line 1, in <module>
+  File "C:\Program Files\Python310\lib\_sitebuiltins.py", line 26, in __call__
+    raise SystemExit(code)
+SystemExit: None
+
+
+ + + +
test0004_None
- - + + + +
+
pt1_4:
+{}
+
+
+
test0005_None
- - + + + +
+
pt1_5:
+{'var2': 'nginx'}
+
+
+
test0006_None
- - + + + +
+
pt1_6:
+{'var2': 'nginx', 'var3': '101', 'var4': '阿凡\n凡凡', 'var5': '{"name": "小红", "age": 22}'}
+
+
+ 总计 6 - 6 + 5 0 - 0 - 1.154秒 - 通过:100.00% + 1 + 0.720秒 + 通过:83.33% diff --git a/Runner/API/DefaultRunner.py b/Runner/API/DefaultRunner.py index 63d8cd2..d6ed031 100644 --- a/Runner/API/DefaultRunner.py +++ b/Runner/API/DefaultRunner.py @@ -1,4 +1,3 @@ -import sys import unittest from typing import Any, Text, Dict, List from Business.Class.HTMLTestRunner import HTMLTestRunner @@ -33,7 +32,7 @@ class ReList(list): if isinstance(item, (int, bool)): try: r = super().__getitem__(item) - if isinstance(r, (list, tuple, set)): + if isinstance(r, (list, tuple, set)): return __class__(r) elif isinstance(r, (dict,)): return ReDict(r) @@ -61,7 +60,7 @@ class ReDict(dict): def __getitem__(self, item): if item in self: r = super().__getitem__(item) - if isinstance(r, (dict,)): + if isinstance(r, (dict,)): return __class__(r) elif isinstance(r, (list, tuple, set)): return ReList(r) @@ -87,10 +86,21 @@ class ReDict(dict): return self +class CustomAssert: + @staticmethod + def assertPreWith(string: str, sub: str): + if not string.startswith(sub): + raise AssertionError('%s does not starts with %s' % (string, sub)) + + @staticmethod + def assertSufWith(string: str, sub: str): + if not string.endswith(sub): + raise AssertionError('%s does not ends with %s' % (string, sub)) + + class TestCase: _g = {} _l = {} - unit = unittest.TestCase() def __init__( self, @@ -104,19 +114,21 @@ class TestCase: tabs = { "HTTPConf": { "fixed": {}, - "views": "A4:C*", + "views": "A4:F*", "field": [ "Name", - "HostWithScheme", - "DefaultHeader" + "Scheme", + "Host", + "DefaultHeader", + "Timeout", + "Session" ] }, "DataBase": { "fixed": {}, - "views": "E4:L*", + "views": "H4:N*", "field": [ "Name", - "Type", "Host", "Port", "User", @@ -137,10 +149,7 @@ class TestCase: ] }, "TestCase": { - "fixed": { - "ItemName": "B3", - "SessMode": "E3" - }, + "fixed": {}, "views": "A7:AH*", "field": [ "Flag", @@ -168,11 +177,11 @@ class TestCase: "HTTPAssertCookie", "HTTPAssertBody", "HTTPAssertJson", - "DatabaseChannel", - "DatabaseSql", - "DatabaseExtract", - "DatabaseAssertData", - "DatabaseAssertCount", + "BaseChannel", + "BaseSql", + "BaseExtract", + "BaseAssertData", + "BaseAssertRows", "DataSet", "Author", "TestTime", @@ -208,21 +217,33 @@ class TestCase: else: init.exit() self.case = testcase - self.http = Request self.dirs = os.path.dirname(workbook) + self.request = Request() + self.session = Session() - self.i = data['TestCase']['fixed']['ItemName'] - self.m = data['TestCase']['fixed']['SessMode'] self.h = data['HTTPConf']['views'] self.b = data['DataBase']['views'] self.d = data['TestData']['views'] self.c = data['TestCase']['views'] self._set_levels(testlves) - self._set_request_mode(self.m) - def _set_request_mode(self, value=None): - self.http = [Request, Session][self._match_mode(value)]() + self.assert_unit = unittest.TestCase() + self.assert_cust = CustomAssert() + self.assert_list = [ + {'sign': ' == ', 'method': self.assert_unit.assertEqual, 'reverse': 0, 'cast': 0}, + {'sign': ' != ', 'method': self.assert_unit.assertNotEqual, 'reverse': 0, 'cast': 0}, + {'sign': ' >= ', 'method': self.assert_unit.assertGreaterEqual, 'reverse': 0, 'cast': float}, + {'sign': ' <= ', 'method': self.assert_unit.assertLessEqual, 'reverse': 0, 'cast': float}, + {'sign': ' =~ ', 'method': self.assert_unit.assertIn, 'reverse': 1, 'cast': str}, + {'sign': ' !~ ', 'method': self.assert_unit.assertNotIn, 'reverse': 1, 'cast': str}, + {'sign': ' > ', 'method': self.assert_unit.assertGreater, 'reverse': 0, 'cast': float}, + {'sign': ' < ', 'method': self.assert_unit.assertLess, 'reverse': 0, 'cast': float}, + {'sign': ' not in ', 'method': self.assert_unit.assertNotIn, 'reverse': 0, 'cast': 0}, + {'sign': ' in ', 'method': self.assert_unit.assertIn, 'reverse': 0, 'cast': 0}, + {'sign': ' *= ', 'method': self.assert_cust.assertPreWith, 'reverse': 0, 'cast': str}, + {'sign': ' =* ', 'method': self.assert_cust.assertSufWith, 'reverse': 0, 'cast': str}, + ] def _set_levels(self, level_list=None): if isinstance(level_list, (list, tuple, str)): @@ -238,24 +259,10 @@ class TestCase: case _: return 50 - @staticmethod - def _match_parm_type(value=None): - match value: - case 'Json': - return 10 - case 'Form': - return 20 - case _: - return 50 - @staticmethod def _match_bool(value=None): return value in ['是', '开启', '打开', 'True', 'true', 'TRUE', True, 'Yes', 'yes', 'YES', 'Y', 'y', '1', 1, 1.0] - @staticmethod - def _match_mode(value=None): - return value in ['会话模式', 'Session', 'session'] - def _match_leve_run(self, value=None): if self.leve_list is None: return True @@ -317,7 +324,7 @@ class TestCase: return False @staticmethod - def _sub_variable(text='', vars_dict=None): + def _sub_variable(text, vars_dict): if not isinstance(text, str): return text for variable_name in re.findall('\${(.*?)}', text): @@ -328,7 +335,8 @@ class TestCase: text = text.replace('${%s}' % variable_name, str(value)) return text - def _sub_variable_auto(self, data=None, vars_dict=None): + def _sub_variable_auto(self, data): + vars_dict = [self._g, self._l] if isinstance(vars_dict, list): d = {} for v in vars_dict: @@ -351,139 +359,184 @@ class TestCase: return data @staticmethod - def _to_string(text): + def _to_string(value): import math - if text is None: + if value is None: return '' try: - if math.isnan(text): + if math.isnan(value): return '' except: pass - if isinstance(text, float) and math.modf(text)[0] == 0: - return str(int(text)) - return str(text) + if isinstance(value, float) and math.modf(value)[0] == 0: + return str(int(value)) + return str(value) + + @staticmethod + def _to_number(value): + import math + if not value: + return 0 + if value is True: + return 1 + try: + if math.isnan(value): + return 0 + except: + pass + return int(float(value)) + + def _extract_variable(self, extract, vars_dict: dict, domain: str | list | dict): + if not extract: return None + if not isinstance(extract, (list, dict)): + extract = auto_decode(extract) + if isinstance(domain, str): + match domain: + case 'locals': + domain = self._l + case 'global': + domain = self._g + vars_dict = {k if not isinstance(k, str) else k.lower(): v for k, v in vars_dict.items()} + import re + for k, v in (extract or {}).items(): + d = re.findall('^([0-9A-Za-z_]+).*?', v)[0] + locals().__setitem__('r', vars_dict[d.lower()]) + domain[str(k)] = str(eval(v.replace(d, 'r', 1))) + return True + + def _assert(self, assert_items, assert_lists): + for assert_item in assert_items: + expect = self._to_string(assert_item['expect']) + if not expect: continue + actual = assert_item['actual'] + if isinstance(actual, (int, bool, str)): + actual = ReText(actual) + if isinstance(actual, (tuple, list, set)): + actual = ReList(actual) + if isinstance(actual, (dict,)): + actual = ReDict(actual) + assert_rows = 0 + assert_real = 0 + for name, value in { + json_encode(None): None, + json_encode(bool(0)): bool(0), + json_encode(bool(1)): bool(1) + }.items(): + locals().__setitem__(name, value) + for line in list(filter(lambda x: x, expect.split("\n"))): + assert_rows += 1 + for oper in assert_lists: + form = self._parse_formula(re.compile('(\$)(?!{).*?').sub('actual', line), oper['sign']) + if not form: + continue + for i in range(len(form)): + form[i] = self._sub_variable_auto(form[i]).replace("\n", "\\n") + assert_real += 1 + oper['reverse'] and form.reverse() + assert_meth = oper['method'] + assert_parm = [eval(form[0]), eval(form[1])] + for i in range(len(assert_parm)): + try: + if assert_parm[i]['_']: assert_parm[i] = str(assert_parm[i]) + except TypeError as e: + pass + cast = oper['cast'] + if cast: assert_parm[i] = cast(assert_parm[i]) + assert_meth(assert_parm[0], assert_parm[1]) + break + if 1 <= assert_real != assert_rows: + raise Exception('wrong assertion statement') + if 0 == assert_real: + expect = self._sub_variable_auto(expect) + actual = str(actual) + assert actual == expect, '%s != %s' % (actual, expect) def test_unit(self, data): if data['HTTPUri']: if not data['HTTPChannel']: raise Exception('channel not set') + # 创建请求 http = self.h.where((self.h['Name'] == data['HTTPChannel']), inplace=False).dropna(how='all').reset_index(drop=True, inplace=False).loc[0].to_dict() res_kwargs = { 'method': data['HTTPMethod'], - 'url': self._sub_variable_auto((http['HostWithScheme'] or '') + data['HTTPUri'], [self._g, self._l]), - 'query': self._sub_variable_auto(auto_decode(data['HTTPQuery']), [self._g, self._l]), + 'url': self._sub_variable_auto((http['Scheme'] or 'http').lower() + '://' + (http['Host'] or '') + data['HTTPUri']), + 'query': self._sub_variable_auto(auto_decode(data['HTTPQuery'])), 'data': None, 'json': None, 'file': None, 'header': {}, - 'cookie': self._sub_variable_auto(auto_decode(data['HTTPCookie']), [self._g, self._l]), + 'cookie': self._sub_variable_auto(auto_decode(data['HTTPCookie'])), 'auth': None, - 'timeout': 15, + 'timeout': self._to_number(http['Timeout']) or 15, 'proxy': None, 'auto_redirect': self._match_bool(data['HTTPRedirect']), 'ignore_cert_error': False, 'debug': True } res_kwargs['header'].update((auto_decode(http['DefaultHeader']) or {})) - res_kwargs['header'].update((self._sub_variable_auto(auto_decode(data['HTTPHeader']), [self._g, self._l]) or {})) - match self._match_parm_type(data['HTTPParamType']): - case 10: + res_kwargs['header'].update((self._sub_variable_auto(auto_decode(data['HTTPHeader'])) or {})) + match str(data['HTTPParamType']).strip().title(): + case 'Json': res_kwargs['json'] = self._sub_variable_auto(auto_decode(data['HTTPParamContent'])) - case 20: + case 'Form': res_kwargs['data'] = self._sub_variable_auto(auto_decode(data['HTTPParamContent'])) case _: res_kwargs['data'] = self._sub_variable_auto(data['HTTPParamContent']) res_kwargs['file'] = locals().setdefault('f', self._sub_variable_auto(auto_decode( data['HTTPParamOfFile']))) and {k: open(os.path.abspath(os.path.join(self.dirs, './%s' % v)), 'rb') for k, v in locals().get('f').items()} - res = self.http.http(**res_kwargs) - for var_name, var_value in { - '_Status': ReText(res['status'] 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()])}), - '_Cookie': ReDict(res['cookie'] or {}).update({'_': "; ".join([k + '=' + (v or '') for k, v in dict(res['cookie'] or {}).items()])}), - '_Body': ReText(res['text'] or ''), - '_Json': ReDict(res['json'] or {}), - json_encode(None): None, - json_encode(bool(0)): bool(0), - json_encode(bool(1)): bool(1) - }.items(): - locals().__setitem__(var_name, var_value) - for k, v in (auto_decode(data['HTTPExtract']) or {}).items(): - d = re.findall('^([0-9A-Za-z_]+).*?', v)[0] - self._g[str(k)] = str(eval('_' + v.replace(d, d.title(), 1))) - for assert_item in [ - {'expect': data['HTTPAssertStatus'], 'actual': locals().get('_Status')}, - {'expect': data['HTTPAssertReason'], 'actual': locals().get('_Reason')}, - {'expect': data['HTTPAssertHeader'], 'actual': locals().get('_Header')}, - {'expect': data['HTTPAssertCookie'], 'actual': locals().get('_Cookie')}, - {'expect': data['HTTPAssertBody'], 'actual': locals().get('_Body')}, - {'expect': data['HTTPAssertJson'], 'actual': locals().get('_Json')}, - ]: - actual = assert_item['actual'] - if isinstance(actual, (int, bool, str)): - actual = ReText(actual) - if isinstance(actual, (tuple, list, set)): - actual = ReList(actual) - if isinstance(actual, (dict, )): - actual = ReDict(actual) - expect = self._to_string(assert_item['expect']) - if expect: - expect_nums = 0 - expect_flag = 0 - for line in list(filter(lambda x: x, expect.split("\n"))): - expect_nums += 1 - for operator in [ - {'sign': ' == ', 'method': 'self.unit.assertEqual', 'reverse': 0}, - {'sign': ' != ', 'method': 'self.unit.assertNotEqual', 'reverse': 0}, - {'sign': ' >= ', 'method': 'self.unit.assertGreaterEqual', 'reverse': 0}, - {'sign': ' <= ', 'method': 'self.unit.assertLessEqual', 'reverse': 0}, - {'sign': ' =~ ', 'method': 'self.unit.assertIn', 'reverse': 1}, - {'sign': ' !~ ', 'method': 'self.unit.assertNotIn', 'reverse': 1}, - {'sign': ' > ', 'method': 'self.unit.assertGreater', 'reverse': 0}, - {'sign': ' < ', 'method': 'self.unit.assertLess', 'reverse': 0}, - {'sign': ' not in ', 'method': 'self.unit.assertNotIn', 'reverse': 0}, - {'sign': ' in ', 'method': 'self.unit.assertIn', 'reverse': 0}, - ]: - formula_args = self._parse_formula(self._sub_variable_auto(line).replace('$', 'actual'), operator['sign']) - if formula_args: - expect_flag += 1 - operator['reverse'] and formula_args.reverse() - _assert = eval(operator['method']) - _assert_parm = [eval(formula_args[0]), eval(formula_args[1])] - for i in range(len(_assert_parm)): - try: - if isinstance(_assert_parm[i], ReDict) and _assert_parm[i]['_']: _assert_parm[i] = str(_assert_parm[i]) - except TypeError as e: - pass - _assert(_assert_parm[0], _assert_parm[1]) - break - if 1 <= expect_flag != expect_nums: - raise Exception('wrong assertion statement') - if 0 == expect_flag: - self.unit.assertEqual(str(actual), expect) - if data['DatabaseSql']: - if not data['DatabaseChannel']: + res = [self.request, self.session][self._match_bool(http['Session'])].http(**res_kwargs) + # 创建变量 + hvar = { + 'Status': ReText(res['status'] 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()])}), + 'Cookie': ReDict(res['cookie'] or {}).update({'_': "; ".join([k + '=' + (v or '') for k, v in dict(res['cookie'] or {}).items()])}), + 'Body': ReText(res['text'] or ''), + 'Json': ReDict(res['json'] or {}), + } + # 提取变量 + self._extract_variable(extract=data['HTTPExtract'], vars_dict=hvar, domain='global') + # 开始断言 + self._assert([ + {'expect': data['HTTPAssertStatus'], 'actual': hvar['Status']}, + {'expect': data['HTTPAssertReason'], 'actual': hvar['Reason']}, + {'expect': data['HTTPAssertHeader'], 'actual': hvar['Header']}, + {'expect': data['HTTPAssertCookie'], 'actual': hvar['Cookie']}, + {'expect': data['HTTPAssertBody'], 'actual': hvar['Body']}, + {'expect': data['HTTPAssertJson'], 'actual': hvar['Json']}, + ], self.assert_list) + if data['BaseSql']: + if not data['BaseChannel']: raise Exception('channel not set') - base = self.b.where((self.b['Name'] == data['DatabaseChannel']), inplace=False).dropna(how='all').reset_index(drop=True, inplace=False).loc[0].to_dict() - # print(base) + base = self.b.where((self.b['Name'] == data['BaseChannel']), inplace=False).dropna(how='all').reset_index(drop=True, inplace=False).loc[0].to_dict() + # 创建请求 conn = MySQL().connect( host=base['Host'], port=int(base['Port']), user=base['User'], password=base['Password'], database=base['Database'], charset=base['Charset'], cursor='Dict' ) - conn.execute(data['DatabaseSql']) - # print(conn.fetchall()) - # print(conn.count()) - for var_name, var_value in { - '_Data': ReList(conn.fetchall() or []) - }.items(): - locals().__setitem__(var_name, var_value) - for k, v in (auto_decode(data['DatabaseExtract']) or {}).items(): - d = re.findall('^([0-9A-Za-z_]+).*?', v)[0] - self._g[str(k)] = str(eval('_' + v.replace(d, d.title(), 1))) - - print(self._g) + conn.execute(data['BaseSql']) + # 创建变量 + bvar = { + 'Data': ReList(conn.fetchall() or []), + 'Rows': conn.count() or 0 + } + # 提取变量 + self._extract_variable(extract=data['BaseExtract'], vars_dict=bvar, domain='global') + # for var_name, var_value in { + # '_Data': ReList(conn.fetchall() or []), + # '_Rows': conn.count() or 0 + # }.items(): + # locals().__setitem__(var_name, var_value) + # for k, v in (auto_decode(data['DatabaseExtract']) or {}).items(): + # d = re.findall('^([0-9A-Za-z_]+).*?', v)[0] + # self._g[str(k)] = str(eval('_' + v.replace(d, d.title(), 1))) + # 开始断言 + self._assert([ + {'expect': data['BaseAssertData'], 'actual': bvar['Data']}, + {'expect': data['BaseAssertRows'], 'actual': bvar['Rows']}, + ], self.assert_list) + print(self._g) # expect = self._to_string(data['HTTPAssertStatus']) # if expect: diff --git a/main4.py b/main4.py index cc24b9d..762fee8 100644 --- a/main4.py +++ b/main4.py @@ -1,9 +1,5 @@ import unittest -# ut = unittest.TestCase() -# ut.assertEqual('1', '1') -# # ut.assertIn('1234', '123') -# ut.assertLessEqual('4','3') -# ut.assert -l = [1, 2] -print(l.reverse()) -print(l) \ No newline at end of file + +unit = unittest.TestCase( +) +unit.assertEqual("阿凡\n凡凡", '123') diff --git a/main5.py b/main5.py new file mode 100644 index 0000000..d9e59e2 --- /dev/null +++ b/main5.py @@ -0,0 +1,11 @@ +def assertPreWith(string: str, sub: str): + if not string.startswith(sub): + raise AssertionError('%s does not starts with %s' % (string, sub)) + + +def assertSufWith(string: str, sub: str): + if not string.endswith(sub): + raise AssertionError('%s does not ends with %s' % (string, sub)) + +assertSufWith('admin 123456 ...', '...') +assertPreWith('admin 123456 ...', 'admin 1') \ No newline at end of file