import unittest
from Business.Class.HTMLTestRunner import HTMLTestRunner
from Business.Class.ExcelUtils import *
from Business.Class.JsonOrYaml import *
from Base.Class.Http import *
from Base.Class.Time import *
from Base.Class.Open import *
from Base.Class.Database import *
from Base.Class.Encrypt import *
from Base.Class.Logger import *
def reparse(regexp, string):
import re
mut = isinstance(regexp, tuple)
reg = [str(regexp), str(regexp[0])][mut]
flg = (mut and eval('re.%s' % str(regexp[1]).upper())) or 0
if reg in ['', 'trim']:
return ReText(string.strip())
else:
return ReList(re.findall(reg, string, flg))
class ReText(str):
def __getitem__(self, item):
if isinstance(item, (int, bool)):
return __class__(super().__getitem__(item))
else:
return reparse(item, str(self.__str__()))
class ReList(list):
def __getitem__(self, item):
if isinstance(item, (int, bool)):
try:
r = super().__getitem__(item)
if isinstance(r, (list, tuple, set)):
return __class__(r)
elif isinstance(r, (dict,)):
return ReDict(r)
else:
return ReText(r)
except Exception:
return __class__()
else:
try:
return reparse(item, str(self.__str__()))
except Exception:
return ReText('')
def __str__(self):
try:
return ReText(self[0])
except Exception:
return ReText('')
class ReDict(dict):
def __getattr__(self, item):
return self.__getitem__(item)
def __getitem__(self, item):
if item in self:
r = super().__getitem__(item)
if isinstance(r, (dict,)):
return __class__(r)
elif isinstance(r, (list, tuple, set)):
return ReList(r)
else:
return ReText(r)
else:
try:
return reparse(item, str(self.__str__()))
except Exception:
return ReText('')
def __str__(self):
try:
if '_' in self:
return ReText(self.__getitem__('_'))
else:
return json_encode(self, indent=None, unicode=False)
except Exception:
return ReText('')
def update(self, *args, **kwargs):
super().update(*args, **kwargs)
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 = {}
def __init__(self, workbook: str, testcase: str, module: str = None, caseid: 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 = {
"HTTPConf": {
"fixed": {},
"views": "A4:H*",
"field": [
"Name",
"Scheme",
"Host",
"DefaultHeader",
"Timeout",
"Session",
"Proxies",
"IgnoreSSLCertError"
]
},
"DataBase": {
"fixed": {},
"views": "J4:P*",
"field": [
"Name",
"Host",
"Port",
"User",
"Password",
"Database",
"Charset"
]
},
"TestCase": {
"fixed": {},
"views": "A7:AH*",
"field": [
"Flag",
"PreExecRule",
"PreExecCase",
"CaseModule",
"CaseId",
"CaseTitle",
"CaseDesc",
"CaseLevel",
"HTTPChannel",
"HTTPMethod",
"HTTPUri",
"HTTPQuery",
"HTTPRedirect",
"HTTPHeader",
"HTTPCookie",
"HTTPParamType",
"HTTPParamContent",
"HTTPParamOfFile",
"HTTPExtract",
"HTTPAssertStatus",
"HTTPAssertReason",
"HTTPAssertHeader",
"HTTPAssertCookie",
"HTTPAssertBody",
"HTTPAssertJson",
"BaseChannel",
"BaseSql",
"BaseExtract",
"BaseAssertData",
"BaseAssertRows",
"DataFileSet",
"TestAuthor",
"TestTime",
"TestResult"
]
}
}
import os
from pandas.core.frame import DataFrame
self.init = Excel().open(workbook)
data = {}
for k, v in tabs.items():
match k:
case 'TestCase':
sheet = testcase
case 'DataBase':
sheet = '请求配置'
case 'HTTPConf':
sheet = '请求配置'
case _:
continue
self.init.select(sheet)
data[k] = {}
if 'fixed' in v.keys():
data[k]['fixed'] = {}
for key, value in v['fixed'].items():
data[k]['fixed'][key] = self.init.cellGet(cell=value)
if 'views' in v.keys() and 'field' in v.keys():
data[k]['views'] = read_view_dict(
filename=workbook, sheet=sheet, area=v['views'], fields=v['field'], auto_truncate=True)
data[k]['ocell'] = read_view_dict(
filename=workbook, sheet=sheet, area=v['views'], fields=v['field'], auto_truncate=True, object_cell=True)
self.module_name = module
self.testcase_id = caseid
self.write_test_result = write_test_result
self.case = '%s_%s' % (testcase, module) if module else testcase
self.dirs = os.path.dirname(workbook)
self.request = Request()
self.session = Session()
self.h_vc = DataFrame(data['HTTPConf']['views']['data']).reset_index(drop=False, inplace=False)
self.b_vc = DataFrame(data['DataBase']['views']['data']).reset_index(drop=False, inplace=False)
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.b_oc = DataFrame(data['DataBase']['ocell']['data']).reset_index(drop=False, inplace=False)
self.c_oc = DataFrame(data['TestCase']['ocell']['data']).reset_index(drop=False, inplace=False)
self.h_excel_object = data['HTTPConf']['ocell']['excel_object']
self.b_excel_object = data['DataBase']['ocell']['excel_object']
self.c_excel_object = data['TestCase']['ocell']['excel_object']
self._set_levels(testlves)
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},
]
self.pre_executed_list = []
self.__realnb__ = 0
self.__currnb__ = 0
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):
if isinstance(level_list, (list, tuple, str)):
self.leve_list = level_list
else:
self.leve_list = None
@staticmethod
def _match_case_type(value=None):
match value:
case '正常用例':
return 10
case '前置用例':
return 20
case '关闭用例' | _:
return 50
@staticmethod
def _match_prex_rule(value=None):
match value:
case '全局一次':
return 10
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]
def _match_leve_run(self, value=None):
if self.leve_list is None:
return True
return value in self.leve_list
def _merge_dict(self, dict_ori, dict_add):
if isinstance(dict_ori, dict) and isinstance(dict_add, dict):
for k, v in dict_ori.items():
if k in dict_add.keys():
if isinstance(v, dict):
dict_ori[k] = self._merge_dict(dict_ori[k], dict_add[k])
else:
dict_ori[k] = dict_add[k]
for k, v in dict_add.items():
if k not in dict_ori.keys():
dict_ori[k] = dict_add[k]
return dict_ori
@staticmethod
def _parse_formula(text, operator):
locals().__setitem__('quota', 0)
none_quota = []
none_range = []
for i in range(len(text)):
this = text[i]
match this:
case '\'':
match locals().get('quota'):
case 0:
locals().__setitem__('quota', 1)
case 1:
locals().__setitem__('quota', 0)
case '\"':
match locals().get('quota'):
case 0:
locals().__setitem__('quota', 2)
case 2:
locals().__setitem__('quota', 0)
case _:
match locals().get('quota'):
case 0:
none_quota.append(i)
for i in range(len(none_quota) + 1):
last = (i > 0 and none_quota[i - 1]) or -1
this = (i < len(none_quota) and none_quota[i]) or 9999
if (this - last) > 1:
none_range.append(last)
none_range.append(this)
none_range.pop(0)
none_range.pop(-1)
none_range_list = []
for i in range(int(len(none_range) / 2)):
none_range_list.append([none_range[i * 2], none_range[i * 2 + 1]])
for v in none_range_list:
p = text.find(operator, v[0], v[1] + 1)
if not p == -1:
return [text[:p], text[p + len(operator):]]
else:
return False
@staticmethod
def _sub_encr(text, encr_dict):
if not isinstance(text, str):
return text
for encr_text in re.findall('([0-9a-zA-Z_]+)\((.*?)\)', text):
if encr_text[0] not in encr_dict.keys():
continue
try:
value = encr_dict[encr_text[0]](encr_text[1])
except Exception:
value = ''
if value is None:
value = ''
text = text.replace('%s(%s)' % (encr_text[0], encr_text[1]), str(value))
return text
def _sub_vars(self, text, vars_dict):
if not isinstance(text, str):
return text
for variable_name in re.findall('\${(.*?)}', text):
try:
value = vars_dict[variable_name]
except Exception:
value = ''
if self._is_nan(value) or value is None:
value = ''
text = text.replace('${%s}' % variable_name, str(value))
return text
def _sub_alls(self, text):
vars_dict = [self._g, self._l]
if isinstance(vars_dict, list):
d = {}
for v in vars_dict:
d.update(v)
vars_dict = d
encr_dict = {
'base64': base64_encode,
'base64_encode': base64_encode,
'base64_decode': base64_decode,
'md5': md5,
'sha1': sha1,
'sha224': sha224,
'sha256': sha256,
'sha384': sha384,
'sha512': sha512,
'urlencode': urlencode,
'urldecode': urldecode,
}
text = self._sub_vars(text=text, vars_dict=vars_dict)
text = self._sub_encr(text=text, encr_dict=encr_dict)
return text
def _sub_variable_auto(self, data):
if isinstance(data, list):
for i in range(len(data)):
if not isinstance(data[i], str):
continue
data[i] = self._sub_alls(text=data[i])
return data
if isinstance(data, dict):
for k in data.keys():
if not isinstance(data[k], str):
continue
data[k] = self._sub_alls(text=data[k])
return data
if isinstance(data, str):
return self._sub_alls(data)
return data
@staticmethod
def _to_string(value):
import math
if value is None:
return ''
if isinstance(value, (float, int)) and str(value) == str(float('NaN')):
return ''
if isinstance(value, (float,)) and math.modf(value)[0] == 0:
return str(int(value))
return str(value)
@staticmethod
def _to_number(value):
if not value:
return 0
if value is True:
return 1
if isinstance(value, (float, int)) and str(value) == str(float('NaN')):
return 0
return int(float(value))
@staticmethod
def _is_nan(value):
if isinstance(value, (float, int)) and str(value) == str(float('NaN')):
return True
else:
return False
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, func_name, data, main=None):
log.d(func_name)
if data['HTTPUri']:
if not data['HTTPChannel']:
raise Exception('channel not set')
from pandas.core.frame import DataFrame
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 = {
'method': data['HTTPMethod'],
'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'])),
'auth': None,
'timeout': self._to_number(http['Timeout']) or 15,
'proxy': self._to_string(http['Proxies']),
'auto_redirect': self._match_bool(data['HTTPRedirect']),
'ignore_cert_error': self._match_bool(http['IgnoreSSLCertError']),
'debug': True
}
res_kwargs['header'].update((auto_decode(http['DefaultHeader']) or {}))
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 '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.request, self.session][self._match_bool(http['Session'])].http(**res_kwargs)
log.d("\n" + res['brief'])
hvar = {
'Status': ReText(res['status'] or ''),
'Reason': ReText(res['reason'] or ''),
'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()])}),
'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_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(
host=base['Host'], port=int(base['Port']),
user=base['User'], password=base['Password'], database=base['Database'], charset=base['Charset'], cursor='Dict'
)
conn.execute(data['BaseSql'])
bvar = {
'Data': ReList(conn.fetchall() or []),
'Rows': conn.count() or 0
}
log.d("\n" + conn.brief())
self._extract_variable(extract=data['BaseExtract'], vars_dict=bvar, domain='global')
self._assert([
{'expect': data['BaseAssertData'], 'actual': bvar['Data']},
{'expect': data['BaseAssertRows'], 'actual': bvar['Rows']},
], self.assert_list)
def test(self, index, update_global=None, update_locals=None, func_name=''):
if isinstance(update_global, (dict,)):
self._g.update(update_global)
if isinstance(update_locals, (dict,)):
self._l.update(update_locals)
def write_test_result(object_cell, test_result):
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 = new_cell_value
self.__currnb__ += 1
if self.write_test_result and self.__currnb__ >= self.__realnb__: self.c_excel_object.save()
main_case_object_cell = self.c_oc.loc[index].to_dict()
try:
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")]))
for case_id in prex_list:
if self._match_prex_rule(main_case['PreExecRule']) == 10 and case_id in self.pre_executed_list:
continue
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']:
raise Exception('Pre-exec case cannot be nested.')
if data['DataFileSet']:
raise Exception('Pre-exec case cannot use data files set.')
self.pre_executed_list.append(case_id)
self._test_unit(func_name=func_name, data=data)
# 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()
write_test_result(main_case_object_cell, 'Passed')
except AssertionError:
self._l and self._l.clear()
write_test_result(main_case_object_cell, 'Failed')
raise
except Exception:
self._l and self._l.clear()
write_test_result(main_case_object_cell, 'Errors')
raise
def main(self):
class ClassTestCase(unittest.TestCase):
pass
ClassTestCase.__qualname__ = self.case
ClassTestCase._test_ = self.test
from pandas import read_csv
while 1:
if self.organize_mode == 20:
levels_list = self.leve_list
loop = len(levels_list)
else:
levels_list = None
loop = 1
break
serial = 0
realnb = 0
for n in range(loop):
filter_module = self.c_vc['CaseModule'] == self.module_name if self.module_name else self.c_vc['CaseModule'] != '*'
filter_caseid = self.c_vc['CaseId'] == self.testcase_id if self.testcase_id else self.c_vc['CaseId'] != '*'
if self.organize_mode == 20:
case = self.c_vc.where(filter_module & filter_caseid & (self.c_vc['CaseLevel'] == levels_list[n]), inplace=False).dropna(how='all')
else:
case = self.c_vc.where(filter_module & filter_caseid, 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(open_auto(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
def func(self, index=data['index'], update_global=None, update_locals=vars_data, func_name=func_name):
return self._test_(index=index, update_global=update_global, update_locals=update_locals, func_name=func_name)
func.__doc__ = func_docs
type.__setattr__(ClassTestCase, func_name, func)
realnb += 1
j += 1
if once_do_break: break
self.__realnb__ = realnb
self.__currnb__ = 0
return ClassTestCase
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
from argparse import ArgumentParser
argparser = ArgumentParser(
argument_default='',
description='API Test Runner',
usage="\n" + os.path.basename(__file__) + ' [-c CONFIG]' +
"\n" + os.path.basename(__file__) + ' [-w WORKBOOK] [-t TESTCASE] [-m MODULE] [-a CASEID] [-l LEVELS] [-o ORGANIZE] [-r WRITE_TEST_RESULT] [-v VERBOSITY] [-p REPORT] [-x LOG] [-i TITLE] [-d DESCRIPTION]'
)
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(
'-a',
required=bool(0), help='You can specify a testcase id.',
choices=None, default=None, dest='caseid'
)
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=[0, 1], default=None, dest='write_test_result', type=int
)
argparser.add_argument(
'-v',
required=bool(0), help='Level of report and log verbosity(0-5), default: 1.',
choices=[0, 1, 2, 3, 4, 5], default=1, 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()
import sys
if len(sys.argv) <= 1:
argparser.print_help()
exit(0)
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, "caseid": args.caseid, "levels": args.levels, "organize": args.organize, "result": args.write_test_result}]
}
testSuite = unittest.TestSuite()
for obj in config['suite']:
testSuite.addTest(unittest.makeSuite(
testCaseClass=TestCase(workbook=obj['workbook'], testcase=obj['testcase'], module=obj['module'], caseid=obj['caseid'], testlves=obj['levels'], organize_mode=obj['organize'], write_test_result=obj['result']).main(), prefix='test'
))
HTMLTestRunner(verbosity=config['verbosity'], report=abs_path(config['report'] or None), log=abs_path(config['log'] or None), title=config['title'] or None, description=config['description'] or None).run(testSuite)