def init_column_main_list_for_table_api_testcase_sub(): """初始化api_testcase_sub.main_list, 可重复执行""" main_objs = ApiTestcaseMainManager.get_testcase_mains() len_main_objs = len(main_objs) print('总计:{}'.format(len_main_objs)) index = 0 for main_obj in main_objs: index += 1 process = index * 100.0 / len_main_objs print('进度:{:.2f}%'.format(process)) sub_list = json_loads(main_obj.sub_list) for sub_id in sub_list: sub_obj = ApiTestcaseSubManager.get_testcase_sub(id=sub_id) if not sub_obj: print('sub_obj not found, main_obj.id:{0}, sub_id:{1}'.format(main_obj.id, sub_id)) break old_main_list_str = sub_obj.main_list if old_main_list_str: old_main_list = json_loads(old_main_list_str) if old_main_list: if main_obj.id not in old_main_list: old_main_list.append(main_obj.id) main_list_str = json_dumps(old_main_list) else: continue else: main_list_str = '[{}]'.format(main_obj.id) else: main_list_str = '[{}]'.format(main_obj.id) print(main_list_str) ApiTestcaseSubManager.update_testcase_sub(sub_id, main_list=main_list_str)
def query_related_cases_by_sub_id(self): try: sub_id = self.data.pop("subId") except KeyError: return make_response({"code": "100", "desc": "入参校验错误"}) sub_obj = ApiTestcaseSubManager.get_testcase_sub(id=sub_id) if not sub_obj or not sub_obj.main_list or not json_loads( sub_obj.main_list): return make_response({"code": "101", "desc": "没有关联的用例信息"}) main_id_list = json_loads(sub_obj.main_list) main_objs = ApiTestcaseMainManager.get_testcases_in_id_list( main_id_list) data_list = [] for main_obj in main_objs: p_obj = ApiProductLineManager.get_product_line( id=main_obj.api_product_line_id) data_dic = { "testcaseId": main_obj.id, "testcaseName": main_obj.testcase_name, "productLineDesc": get_full_product_line_name(p_obj) } data_list.append(data_dic) return make_response({"code": "000", "data": data_list})
def _load_main_testcase(self, testcase_main_id, testset=None, custom_flow_id=None): """ 循环加载子用例到testset """ tm_obj = atmm.get_testcase_main(id=testcase_main_id) tm_obj.testcase_name = '{0}__{1}'.format(tm_obj.testcase_name, tm_obj.expect_result) sub_list = json_loads(tm_obj.sub_list) if custom_flow_id: # 存在指定自定义链路信息,按照链路切割sub_list flow_obj = atmcfm.get_flow(id=custom_flow_id) # sub_list = sub_list[flow_obj.start_sub_index:flow_obj.start_sub_index + 1] flow_index_list = json_loads(flow_obj.flow_index_list) new_sub_list = [sub_list[i] for i in flow_index_list] sub_list = new_sub_list sub_list.reverse() # 新加载方式,耗时减少60% union_list = split_list_to_union_list(sub_list) # print(union_list) sub_objs = [] for part_list in union_list: part_sub_objs = atsm.get_testcase_subs_in_id_list(part_list) sorted_part_sub_objs = sort_objs_by_id_list(part_sub_objs, part_list) sub_objs.extend(sorted_part_sub_objs) intf_id_list = [sub_obj.api_intf_id for sub_obj in sub_objs] union_intf_id_list = split_list_to_union_list(intf_id_list) intf_objs = [] for part_list in union_intf_id_list: part_intf_objs = aiim.get_intfs_in_id_list(part_list) sorted_part_intf_objs = sort_objs_by_id_list(part_intf_objs, part_list) intf_objs.extend(sorted_part_intf_objs) for i in range(len(sub_objs)): sub_obj = sub_objs[i] print('sub_id:{}'.format(sub_obj.id)) ts_obj = sub_obj intf_obj = intf_objs[i] # print('intf_id:{}'.format(intf_obj.id)) testset = self._add_to_testset(testset, tc_obj=ts_obj, intf_obj=intf_obj, tm_obj=tm_obj) # # 旧加载方式 # for sub_id in sub_list: # print('sub_id:{}'.format(sub_id)) # ts_obj = atsm.get_testcase_sub(id=sub_id) # intf_obj = aiim.get_intf(id=ts_obj.api_intf_id) # # testset = self._add_to_testset(testset, tc_obj=ts_obj, intf_obj=intf_obj, tm_obj=tm_obj) # 接口用例(case_type==1)可能存在前置全链路用例(目前还没有) if tm_obj.case_type == 1: setup_flow_list = json_loads(tm_obj.setup_flow_list) setup_flow_list.reverse() for flow_id in setup_flow_list: testset = self._load_main_testcase(flow_id, testset=testset, custom_flow_id=custom_flow_id) return testset
def init_column_for_table_api_testcase_info(): """初始化api_testcase_info表的setup_case_list列数据""" to_update_rows = [] objs = atim.get_testcases() for obj in objs: if obj.setup_case_list: continue include_str = obj.include if include_str: include_list = json_loads(include_str) for include in include_list: if 'setup_cases' in include and include['setup_cases'] and isinstance(include['setup_cases'], list): setup_case_list = [] for case_id_str in include['setup_cases']: setup_case_list.append('1-' + case_id_str) to_update_rows.append( { 'id': obj.id, 'setup_case_list': json_dumps(setup_case_list) } ) print(json_dumps(to_update_rows)) print(len(to_update_rows)) x_len = len(to_update_rows) index = 0 for row in to_update_rows: atim.update_testcase(id_=row['id'], setup_case_list=row['setup_case_list']) index += 1 print(index*100.0/x_len)
def delete_variable(self): """根据变量名称(唯一)删除变量 input: { "variable_name":"test", "systemId":"1" } """ try: variable_id = int(self.data.pop("variableId")) # id_=data.pop("id") except KeyError: return make_response({"code": "100", "desc": "入参校验失败"}) pv_obj = self.apv.get_variable(id=variable_id) if not pv_obj: return make_response({ "code": "000", "desc": "变量id{}不存在,请刷新后重试".format(variable_id) }) include_list = self.atim.query_all_testcases_include() for row in include_list: if not row[0]: continue include = json_loads(row[0]) if 'public_variables' in include[0]: if variable_id in include[0]['public_variables']: return make_response({ "code": "200", "desc": "存在用例引用公共变量,无法直接删除" }) self.apv.delete_variable(pv_obj.id) return make_response({"code": "000", "desc": "变量删除成功"})
def _init_testset(self, tc_obj, intf_obj, tm_obj=None): """ 初始化testset """ testset = self._get_testset_from_obj(tc_obj, intf_obj, tm_obj=tm_obj) testset = self._add_public_env_to_config(testset) testset = self._param_check_update(testset, tc_obj) # 主用例 if tm_obj: main_case_id = tm_obj.id main_name = tm_obj.testcase_name testset['config']['is_main'] = True # testset增加主用例用例后置信息 if tm_obj.main_teardown_hooks: main_teardown_hooks = json_loads(tm_obj.main_teardown_hooks) if main_teardown_hooks: parsed_teardown_hooks = [] for teardown_hook_str in main_teardown_hooks: parsed_teardown_hook_str = setup_or_teardown_string_add_param(teardown_hook_str, is_setup=False) parsed_teardown_hooks.append(parsed_teardown_hook_str) testset['config']['teardown_hooks'] = parsed_teardown_hooks # 接口用例 else: main_case_id = tc_obj.id main_name = tc_obj.testcase_name testset['config']['is_main'] = False testset['name'] = main_name testset['config']['case_id'] = main_case_id # 给testset增加name信息 testset['config']['name'] = testset['name'] return testset
def setup_qa_create_accounting_overdue(loan_id, overdue_days, env_name): """ 其他-根据偏移天数更新贷款数据 : 贷款编号, 偏移天数 :param loan_id: 贷款编号:贷款编号loan_id :param overdue_days: 偏移天数:正整数-期望逾期的天数;负数-期望往前偏移的天数 :param env_name: 环境名称 :return: """ if not isinstance(overdue_days, int): raise Exception('偏移天数输入有误,只支持数字') if overdue_days < 0: dt = datetime.datetime.now() + relativedelta(days=overdue_days) else: dt = datetime.datetime.now() + relativedelta(days=-overdue_days) + relativedelta(months=-1) loan_timestamp = int(time.mktime(dt.timetuple())) data = {"env": env_name, "loanDate": loan_timestamp, "loanId": loan_id} url = "http://****/atp/qa/createAccountingOverdue" header_info = {"Content-Type": "application/json"} hc = HttpClient() try: r = hc.http_post(url, header_info, data) r_dic = json_loads(r.text) except Exception as err: raise Exception('出现未知错误:{0}'.format(repr(err))) if r_dic['code'] == '000': return True else: raise Exception(r_dic['desc'])
def delete_testcase_main(self): try: testcase_id = int(self.data.pop('testcaseId')) except KeyError: return make_response({"code": "100", "desc": CODE_DESC_MAP["100"]}) tm_obj = self.atmm.get_testcase_main(id=testcase_id) if not tm_obj: return make_response({ "code": "200", "desc": "用例id\"{}\"不存在, 请刷新后重试".format(testcase_id) }) sub_list = json_loads(tm_obj.sub_list) if tm_obj.case_type == 2: for sub_id in sub_list: sub_obj = self.atsm.get_testcase_sub(id=sub_id) main_list = json_loads( sub_obj.main_list) if sub_obj.main_list else [] if testcase_id in main_list: main_list.remove(testcase_id) self.atsm.update_testcase_sub( sub_id, main_list=json_dumps(main_list)) # self.atsm.delete_testcase_sub(sub_id) else: for sub_id in sub_list: sub_obj = self.atsm.get_testcase_sub(id=sub_id) if sub_obj.api_intf_id == tm_obj.api_intf_id: main_list = json_loads( sub_obj.main_list) if sub_obj.main_list else [] if testcase_id in main_list: main_list.remove(testcase_id) self.atsm.update_testcase_sub( sub_id, main_list=json_dumps(main_list)) # self.atsm.delete_testcase_sub(sub_id) self.atmm.delete_testcase_main(testcase_id) # 删除tag关系 relation_objs = self.atmtrm.get_relations(api_testcase_id=testcase_id) for relation_obj in relation_objs: self.atmtrm.delete_relation(relation_obj.id) return make_response({"code": "000", "desc": "测试用例删除成功"})
def migrate_base(step): """ 迁移基线用例数据 前置条件:先把 base_testcase_info, base_module_info表数据分别导出到相应备份表,并清空 base_module_info表 注意按顺序执行,中途报错不可继续 """ if step == 1: # migrate testcase bt_objs = btm.get_all_testcase() process_len = len(bt_objs) process_id = 0 for bt_obj in bt_objs: process_id += 1 print('{:.1f}%'.format(process_id * 100.0 / process_len)) id_ = bt_obj.id print(id_) try: detail_dic = json_loads(bt_obj.detail) except Exception: detail_dic = {} if isinstance(detail_dic, list): continue new_detail_dic = _change_base_detail(detail_dic) btm.update_base_testcase(id_, detail=json_dumps(new_detail_dic)) if step == 2: # migrate module bm_bak_objs = bmm_bak.get_modules() process_len = len(bm_bak_objs) process_id = 0 for bm_bak_obj in bm_bak_objs: process_id += 1 print('{:.1f}%'.format(process_id * 100.0 / process_len)) if bm_bak_obj.system_id: bmm.insert_base_module(id=bm_bak_obj.id, module_name=bm_bak_obj.module_name, system_id=bm_bak_obj.system_id) second_bm_objs = bmm_bak.get_modules(parent_module_id=bm_bak_obj.id) if not second_bm_objs: new_module_name = bm_bak_obj.module_name bt_objs = btm.get_all_testcase(module_id=bm_bak_obj.id) if bt_objs: bmm.insert_base_module(module_name=new_module_name, parent_module_id=bm_bak_obj.id) second_bm_obj = bmm.get_module(module_name=new_module_name, parent_module_id=bm_bak_obj.id) new_module_id = second_bm_obj.id for bt_obj in bt_objs: btm.update_base_testcase(bt_obj.id, module_id=new_module_id) else: sub_bm_bak_objs = bmm_bak.get_modules(parent_module_id=bm_bak_obj.id) if sub_bm_bak_objs: continue else: new_module_name, new_parent_id = _change_name_and_parent_id(bm_bak_obj.id) bmm.insert_base_module(id=bm_bak_obj.id, module_name=new_module_name, parent_module_id=new_parent_id)
def field_check_not_in_list(check_value, expect_value): """ 接口响应-字段为list类型且不包含期望值 : 校验, 预期结果 :desc: 说明: 验证返回报文中的某个字段的值的类型是list,并且不包含期望值 :param check_value:待校验内容: 指定返回报文中的某个字段,支持多层级,例如 content.demoList :param expect_value: 期望值: 期望不被包含的值 :return: """ # 返回true和false,关联测试报告success和fail try: if isinstance(check_value, list): if expect_value not in check_value: # json like if (expect_value.startswith('[') and expect_value.endswith(']')) or (expect_value.startswith('{') and expect_value.endswith('}')): try: expect_value = json_loads(expect_value) except json.decoder.JSONDecodeError: return True if expect_value not in check_value: return True else: return False, '待校验的字段值是{0}, 包含了期望值{1}'.format(check_value, expect_value) # num like elif expect_value.isdigit(): try: expect_value = int(expect_value) except ValueError: return True if expect_value not in check_value: return True else: return False, '待校验的字段值是{0}, 包含了期望值{1}'.format(check_value, expect_value) # True/False like elif 'rue' in expect_value or 'alse' in expect_value: if expect_value == 'true' or expect_value == 'True': expect_value = True if expect_value not in check_value: return True else: return False, '待校验的字段值是{0}, 包含了期望值{1}'.format(check_value, expect_value) elif expect_value == 'false' or expect_value == 'False': expect_value = False if expect_value not in check_value: return True else: return False, '待校验的字段值是{0}, 包含了期望值{1}'.format(check_value, expect_value) else: return True else: return True else: return False, '待校验的字段值是{0}, 包含了期望值{1}'.format(check_value, expect_value) else: return False, '待校验的字段值是{0}, 类型是{1}'.format(check_value, type(check_value)) except Exception as err: return False, err.args[0]
def httprunner_request_update(): """已废弃,请勿执行""" from atp.api.mysql_manager import TestcaseInfoManager tc_objs = TestcaseInfoManager.get_all_testcases() count = 0 for tc_obj in tc_objs: if tc_obj: if tc_obj.request: request = json_loads(tc_obj.request) count += 1 new_request = handle_step_dic(request) print(count) print(json_dumps(new_request))
def get_custom_flows(self): try: testcase_id = self.data.pop('testcaseId') except KeyError: return make_response({"code": "100", "desc": CODE_DESC_MAP["100"]}) flow_objs = ApiTestcaseMainCustomFlowManager.get_flows( testcase_id=testcase_id) data_list = [{ 'flowId': obj.id, 'flowName': obj.flow_name, 'flowIndexList': json_loads(obj.flow_index_list) } for obj in flow_objs] return make_response({"code": "000", "flowList": data_list})
def get_testcase_id_list_filter_by_tag(self, intf_id=None, product_line_id=None): """ 根据任务配置的测试标签,过滤接口用例和全链路用例""" try: related_tag_id_list = json_loads(self.task_obj.related_tag_id_list) except Exception: related_tag_id_list = None if not related_tag_id_list: return [] if intf_id: res = ApiTestcaseInfoManager.filter_task_testcase_ids(intf_id, related_tag_id_list) return [row[0] for row in res] elif product_line_id: res = ApiTestcaseMainManager.filter_task_testcase_ids(product_line_id, related_tag_id_list) return [row[0] for row in res]
def _load_api_testcase(self, testcase_id, testset=None): """ 递归加载接口用例到testset """ tc_obj = atim.get_testcase(id=testcase_id) tc_obj.testcase_name = '{0}__{1}'.format(tc_obj.testcase_name, tc_obj.expect_result) intf_obj = aiim.get_intf(id=tc_obj.api_intf_id) include_list = json_loads(tc_obj.include) setup_cases_list = include_list[1]["setup_cases"] testset = self._add_to_testset(testset, tc_obj, intf_obj) if setup_cases_list: """存在前置用例""" for setup_case_id in reversed(setup_cases_list): """前置用例id不能和自身用例id相同""" if str(setup_case_id) != str(testcase_id): """加载前置用例到testset""" testset = self._load_api_testcase(setup_case_id, testset) return testset
def _param_check_update(testset, tc_obj): """ 识别是否"入参字段校验用例", 是:更新teststeps结构 """ include_list = json_loads(tc_obj.include) if include_list and isinstance(include_list, list) and len(include_list) >= 3: param_check_dict = include_list[2]["param_check"] original_step = copy.deepcopy(testset["teststeps"][0]) step_name = original_step['name'][:-2] if original_step['name'].endswith('.1') else original_step['name'] step_no = 1 for item, check_key_list in param_check_dict.items(): # 入参值为空字符串校验 if item == 'empty': testset["teststeps"] = [] for key in check_key_list: copy_step = copy.deepcopy(original_step) if key in copy_step['request']['json']: copy_step['name'] = '{0}.{1}'.format(step_name, step_no) copy_step['request']['json'][key] = '' testset["teststeps"].append(copy_step) step_no += 1 return testset
def _add_step_to_testset(self, testset, tc_obj, intf_obj, tm_obj=None): """ 添加tc_obj的step到testset """ # testset增加主用例用例后置信息 if tm_obj and tm_obj.main_teardown_hooks: main_teardown_hooks = json_loads(tm_obj.main_teardown_hooks) if main_teardown_hooks: parsed_teardown_hooks = [] for teardown_hook_str in main_teardown_hooks: parsed_teardown_hook_str = setup_or_teardown_string_add_param(teardown_hook_str, is_setup=False) parsed_teardown_hooks.append(parsed_teardown_hook_str) if 'teardown_hooks' not in testset['config']: testset['config']['teardown_hooks'] = parsed_teardown_hooks else: testset['config']['teardown_hooks'].extend(parsed_teardown_hooks) testset['config']['teardown_hooks'] = list(set(testset['config']['teardown_hooks'])) setup_testset = self._get_testset_from_obj(tc_obj, intf_obj, tm_obj=tm_obj) setup_teststep = setup_testset['teststeps'][0] testset['teststeps'].insert(0, setup_teststep) return testset
def repair_sub_case_public_var_error(): import re variable_regexp = r"\$([\w_]+)" sub_objs = ApiTestcaseSubManager.get_testcase_subs() repair_count = 0 for sub_obj in sub_objs: if not sub_obj.include or sub_obj.include in ['[]', '[{"public_variables": []}]']: continue include_list = json_loads(sub_obj.include) pv_id_list = include_list[0]['public_variables'] pv_dic = {} for public_v_id in pv_id_list: pv_obj = ApiPublicVariableInfoManager.get_variable(id=public_v_id) pv_dic[public_v_id] = pv_obj.variable_name request_obj = ApiTestcaseRequestQllManager.get_request(api_testcase_id=sub_obj.id) new_pv_id_list = [] if request_obj and request_obj.request: variables = re.findall(variable_regexp, str(request_obj.request)) variables = list(set(variables)) for variable_name in variables: for pv_id, pv_name in pv_dic.items(): if pv_name == variable_name: new_pv_id_list.append(pv_id) break if set(pv_id_list) != set(new_pv_id_list): new_pv_id_list.append(137) if set(pv_id_list) != set(new_pv_id_list): if 137 not in pv_id_list: new_pv_id_list.remove(137) print('sub_obj.id:{2}, old:{0}, new:{1}'.format(pv_id_list, new_pv_id_list, sub_obj.id)) new_include = [{"public_variables": new_pv_id_list}] include_str = json_dumps(new_include) print(include_str) ApiTestcaseSubManager.update_testcase_sub(id_=sub_obj.id, include=include_str) repair_count += 1 print(repair_count)
def merge_sub_case(): """合并相同的sub_case, 可重复执行""" to_update_main_sub_list_dic = {} testcase_main_objs = ApiTestcaseMainManager.get_testcase_mains() main_sub_list_dic = {} for testcase_main_obj in testcase_main_objs: main_sub_list_dic[testcase_main_obj.id] = json_loads(testcase_main_obj.sub_list) redundant_requests = ApiTestcaseRequestQllManager.get_redundant_requests() len_redundant_requests = len(redundant_requests) print('总计:{}'.format(len_redundant_requests)) index = 0 for row in redundant_requests: index += 1 process = index*100.0/len_redundant_requests print('进度:{:.2f}%'.format(process)) print('开始处理:{}'.format(row[0])) request = row[0] request_objs = ApiTestcaseRequestQllManager.get_requests(request=request) print('重复个数:{}'.format(len(request_objs))) target_sub_id = request_objs[0].api_testcase_id for request_obj in request_objs: print(request_obj.api_testcase_id) to_update_sub_id = request_obj.api_testcase_id if to_update_sub_id == target_sub_id: continue else: for main_id in main_sub_list_dic: if to_update_sub_id in main_sub_list_dic[main_id]: new_sub_list = [ target_sub_id if i == to_update_sub_id else i for i in main_sub_list_dic[main_id] ] main_sub_list_dic[main_id] = copy.deepcopy(new_sub_list) print(new_sub_list) to_update_main_sub_list_dic[main_id] = new_sub_list for main_id, sub_list in to_update_main_sub_list_dic.items(): ApiTestcaseMainManager.update_testcase_main(main_id, sub_list=json_dumps(sub_list))
def _add_public_env_to_config(self, testset): """ 给testset增加公共环境信息 """ if testset['config']['variables']: return testset env_info = self.env_info variables_list = testset['config']['variables'] variables_list.append({"DUBBO_ZOOKEEPER": env_info.dubbo_zookeeper}) variables_list.append({"ENV_NAME": env_info.env_name}) variables_list.append({"DB_CONNECT": env_info.db_connect}) variables_list.append({"BASE_HOST": env_info.base_host}) variables_list.append({"REMOTE_HOST": env_info.remote_host}) variables_list.append({"DISCONF_HOST": env_info.disconf_host}) variables_list.append({"REDIS_CONNECT": env_info.redis_connect}) variables_list.append({"SERVER_APP_MAP": env_info.server_app_map}) variables_list.append({"SERVER_DEFAULT_USER": env_info.server_default_user}) mq_key = json_loads(env_info.mq_key) variables_list.append({"MQ_AK": mq_key["ak"]}) variables_list.append({"MQ_SK": mq_key["sk"]}) return testset
def _add_intf_info(self, testset, intf_obj, is_first=False): """ 添加接口基本信息 """ intf_type = intf_obj.intf_type intf_info_dic = json_loads(intf_obj.intf_info) # teststep = testset["teststeps"][0] for teststep in testset["teststeps"]: if intf_type == "HTTP": base_url = self.env_info.base_host api_url = intf_info_dic["apiUrl"].strip() if absolute_http_url_regexp.match(api_url): teststep["request"]["url"] = api_url elif api_url.startswith('/'): teststep["request"]["url"] = base_url + api_url else: teststep["request"]["url"] = base_url + '/' + api_url teststep["request"]["method"] = intf_info_dic["method"] teststep["request"]["headers"] = json_loads(intf_info_dic["headers"].replace("'", "\"")) teststep["request"]["allow_redirects"] = False elif intf_type == "DUBBO": base_url = self.env_info.remote_host teststep["request"]["url"] = base_url + "/invokeDubbo" teststep["request"]["method"] = "POST" teststep["variables"].append({"DUBBO_METHOD": intf_info_dic["dubboMethod"]}) teststep["variables"].append({"DUBBO_INTERFACE": intf_info_dic["dubboService"]}) teststep["variables"].append({"DUBBO_VERSION": intf_info_dic["version"]}) teststep["request"]["json"]["version"] = "$DUBBO_VERSION" teststep["request"]["json"]["interfaceName"] = "$DUBBO_INTERFACE" teststep["request"]["json"]["zkUrl"] = "$DUBBO_ZOOKEEPER" teststep["request"]["json"]["methodName"] = "$DUBBO_METHOD" teststep["request"]["json"]["parameterTypes"] = [] parameter_type_count = 0 try: parameter_type_list = json_loads(intf_info_dic["parameterTypes"]) except json.decoder.JSONDecodeError: logger.error('ERROR parameterTypes: intf_id {}'.format(intf_obj.id)) parameter_type_list = [] for parameter_type in parameter_type_list: parameter_type_count += 1 teststep["variables"].append({ "DUBBO_PARAMETER_TYPE_{no}".format(no=parameter_type_count): parameter_type }) teststep["request"]["json"]["parameterTypes"].append( "$DUBBO_PARAMETER_TYPE_{no}".format(no=parameter_type_count) ) elif intf_type == "MQ": if self.env_info.env_name == "SIT": default_env = self.env_info.env_name else: default_env = "ALIUAT" # MQ的env默认为定值'ALIUAT' base_url = self.env_info.remote_host teststep["request"]["url"] = base_url + "/sendMQ" teststep["request"]["method"] = "POST" teststep["variables"].append({"MQ_TOPIC": intf_info_dic["topic"]}) teststep["variables"].append({"MQ_TAG": intf_info_dic["tag"]}) teststep["variables"].append({"MQ_PID": "PID_{mid}_{env}".format( mid=intf_info_dic["topic"][3:], env=default_env)}) teststep["request"]["json"]["env"] = default_env teststep["request"]["json"]["topic"] = "$MQ_TOPIC" teststep["request"]["json"]["tag"] = "$MQ_TAG" teststep["request"]["json"]["pid"] = "$MQ_PID" teststep["request"]["json"]["onsSecretKey"] = "{{\"{env}\":\"$MQ_SK\"}}".format(env=default_env) teststep["request"]["json"]["onsAccessKey"] = "{{\"{env}\":\"$MQ_AK\"}}".format(env=default_env) if "appid" in intf_info_dic and intf_info_dic["appid"]: teststep["request"]["json"]["appid"] = intf_info_dic["appid"] # 非首次加载接口信息的情况,只处理testset["teststeps"][0] if not is_first: break return testset
def http_runner_run(**kwargs): """调用HttpRunner运行测试""" log_dir = kwargs.pop('log_dir') env_id = kwargs.pop('env_id') testset = kwargs.pop('testset') test_meta_list = kwargs.pop('test_meta_list') run_task_result_id = kwargs.pop('run_task_result_id') intf_id = kwargs.pop('intf_id', None) main_case_id = kwargs.pop('main_case_id', None) main_case_id_list = kwargs.pop('main_case_id_list', None) if intf_id: log_path = '{0}task_run_{1}_intf_{2}.log'.format(log_dir, run_task_result_id, intf_id) elif main_case_id: log_path = '{0}task_run_{1}_main_case_{2}.log'.format(log_dir, run_task_result_id, main_case_id) else: log_path = '{0}task_run_{1}_main_case_list_{2}.log'.format(log_dir, run_task_result_id, main_case_id_list) # 初始化hr_runner hr_kwargs = { "failfast": True, "log_path": log_path } hr_runner = HttpRunner(**hr_kwargs) start_time = time.strftime('%Y-%m-%d %H-%M-%S', time.localtime(time.time())) hr_logger.log_warning("【START】测试开始! (ง •_•)ง") hr_logger.log_warning("【环境】: {}".format(env_id)) # time.sleep(3) try: testset_json = json_dumps(testset) except Exception: testset_json = testset # 执行测试 try: # hr_logger.log_warning("【调用HttpRunner】: {0}".format(testset_json)) hr_runner.run(testset) hr_logger.log_info("【结束调用HttpRunner】") except Exception: raise Exception(traceback.format_exc()) for detail in hr_runner.summary["details"]: for record in detail["records"]: record["meta_data"]["request"].pop("files", None) # 去除summary中的文件对象 summary_remove_file_obj(hr_runner.summary) # 完善summary summary = deepcopy(hr_runner.summary) # summary = hr_runner.summary perfect_summary(summary, test_meta_list) # print(json_dumps(summary)) summary = add_memo(summary) # 识别错误 # print(json_dumps(summary)) summary = identify_errors(summary) return {"summary": json_loads(json_dumps(summary)), "run_task_result_id": run_task_result_id, 'log_dir': log_dir}
def copy_testcase(self): try: testcase_id = self.data.pop('id') copy_num = int(self.data.pop('copyNum')) copy_type = int(self.data.pop('copyType', 1)) # 1:复制主用例,引用子用例 2:复制主用例和子用例 except (KeyError, ValueError): return make_response({"code": "100", "desc": "入参校验失败"}) tm_obj = self.atmm.get_testcase_main(id=testcase_id) if tm_obj.case_type == 2: product_line_id = tm_obj.api_product_line_id pre_obj = self.atmm.get_last_obj_by_product_line(product_line_id) else: intf_id = tm_obj.api_intf_id pre_obj = self.atmm.get_last_obj_by_intf(intf_id) index = pre_obj.index + 1 if pre_obj else 0 table_last_obj = self.atmm.get_last_obj() insert_id = table_last_obj.id + 1 if table_last_obj else 1 if copy_type == 2: from_sub_list = json_loads(tm_obj.sub_list) sub_info_list = [] for from_sub_id in from_sub_list: t_sub_obj = self.atsm.get_testcase_sub(id=from_sub_id) tr_obj = self.atrqm.get_request(api_testcase_id=from_sub_id) sub_info_list.append({ 'request': tr_obj.request, 'sub_name': t_sub_obj.sub_name, 'request_type': t_sub_obj.request_type, 'include': t_sub_obj.include, 'simple_desc': t_sub_obj.simple_desc, 'case_type': t_sub_obj.case_type, 'api_intf_id': t_sub_obj.api_intf_id, 'creator': self.username, 'expect_result': t_sub_obj.expect_result, }) testcase_insert_list = [] testcase_id_list = [] for i in range(copy_num): update_list = copy.deepcopy(sub_info_list) to_sub_list = ApiTestcaseSubManager.batch_update_testcase_sub( update_list) case_name = tm_obj.testcase_name + '_copy_{0}_{1}'.format( testcase_id, i + 1) testcase_insert_list.append({ 'id': insert_id + i, 'testcase_name': case_name, 'simple_desc': tm_obj.simple_desc, 'case_type': tm_obj.case_type, 'case_status': tm_obj.case_status, 'api_intf_id': tm_obj.api_intf_id, 'api_product_line_id': tm_obj.api_product_line_id, 'sub_list': json_dumps(to_sub_list), 'creator': self.username, 'expect_result': tm_obj.expect_result, 'index': index + i, 'setup_flow_list': tm_obj.setup_flow_list, 'main_teardown_hooks': tm_obj.main_teardown_hooks, }) testcase_id_list.append(insert_id + i) self.atmm.batch_insert_testcase_main(testcase_insert_list) # 复制tag tag_relation_objs = self.atmtrm.get_relations( api_testcase_id=testcase_id) tag_id_list = [str(obj.tag_id) for obj in tag_relation_objs] tag_relation_insert_list = [] for i in range(copy_num): for tag_id in tag_id_list: tag_relation_insert_list.append({ 'api_testcase_id': testcase_id_list[i], 'tag_id': tag_id }) self.atmtrm.batch_insert_relation(tag_relation_insert_list) elif copy_type == 1: to_sub_list = json_loads(tm_obj.sub_list) testcase_insert_list = [] testcase_id_list = [] for i in range(copy_num): case_name = tm_obj.testcase_name + '_copy_{0}_{1}'.format( testcase_id, i + 1) testcase_insert_list.append({ 'id': insert_id + i, 'testcase_name': case_name, 'simple_desc': tm_obj.simple_desc, 'case_type': tm_obj.case_type, 'case_status': tm_obj.case_status, 'api_intf_id': tm_obj.api_intf_id, 'api_product_line_id': tm_obj.api_product_line_id, 'sub_list': json_dumps(to_sub_list), 'creator': self.username, 'expect_result': tm_obj.expect_result, 'index': index + i, 'setup_flow_list': tm_obj.setup_flow_list, 'main_teardown_hooks': tm_obj.main_teardown_hooks, }) testcase_id_list.append(insert_id + i) self.atmm.batch_insert_testcase_main(testcase_insert_list) # 复制tag tag_relation_objs = self.atmtrm.get_relations( api_testcase_id=testcase_id) tag_id_list = [str(obj.tag_id) for obj in tag_relation_objs] tag_relation_insert_list = [] for i in range(copy_num): for tag_id in tag_id_list: tag_relation_insert_list.append({ 'api_testcase_id': testcase_id_list[i], 'tag_id': tag_id }) self.atmtrm.batch_insert_relation(tag_relation_insert_list) else: return make_response({ "code": "101", "desc": "错误的copy_type:{0}".format(copy_type) }) return make_response({ "code": "000", "desc": "用例{0}复制成功, 数量{1}".format(testcase_id, copy_num) })
def init_table_api_intf_default_request(): """初始化api_intf_default_request表""" intf_objs = aiim.get_intfs() # tc_objs = atim.get_testcases() process_len = len(intf_objs) process_id = 0 for intf_obj in intf_objs: print('intf_id: {}'.format(intf_obj.id)) t1 = time.time() process_id += 1 print('{:.1f}%'.format(process_id*100.0/process_len)) if aidrm.get_request(api_intf_id=intf_obj.id): continue tc_objs = atim.get_testcases(api_intf_id=intf_obj.id) final_body = None for tc_obj in tc_objs: tc_request_obj = atrm.get_request(api_testcase_id=tc_obj.id) if not tc_request_obj or not tc_request_obj.request: continue request_dic = json_loads(tc_request_obj.request) try: if tc_obj.type == 1: body = request_dic['teststeps'][0]['request']['json'] if not final_body: final_body = body else: final_body = merge_request_body(final_body, body) elif tc_obj.type == 2: body = request_dic['teststeps'][0]['request']['json']['args'] if not final_body: final_body = body else: final_body = merge_request_body(final_body, body, is_list=True) elif tc_obj.type == 3: body_str = request_dic['teststeps'][0]['request']['json']['msg'] body = json_loads(body_str) if not final_body: final_body = body else: final_body = merge_request_body(final_body, body) else: continue except KeyError: print('Error!!') continue t3 = time.time() d_time = t3 - t1 print("==== Finish t3, run {:.3}s ====\n".format(d_time)) if final_body is not None: body = remove_var_mark(final_body) p = ParseBodyToDetail(body) p.parse_main() detail_str = json_dumps(p.detail) body_str = json_dumps(body) aidrm.insert_request(api_intf_id=intf_obj.id, request=body_str, request_detail=detail_str)
def _get_testset_from_obj(tc_obj, intf_obj, tm_obj=None): """ 从数据库对象obj中获取request值,并转成dict格式,并进行一些预处理,把缺少必要环境变量的地方加上环境变量 e.g: change from "variables": [ { "encrypted_newPassword": "******" } ], to "variables": [ { "encrypted_newPassword": "******" } ], 更新:识别"入参字段校验用例" """ if tm_obj: testset_str = atrqm.get_request(api_testcase_id=tc_obj.id).request else: testset_str = atrm.get_request(api_testcase_id=tc_obj.id).request try: testset = json.loads(testset_str) except JSONDecodeError: raise LoadCaseError('Json Load testcase_info.request Error') # for v_dic in testset["teststeps"][0]["variables"]: # for v_name in v_dic: # if isinstance(v_dic[v_name], str) and v_dic[v_name].startswith( # "${encrypt_by_public_key") and not v_dic[v_name].endswith( # "||$REMOTE_HOST)}"): # v_dic[v_name] = v_dic[v_name][:-2] + "||$REMOTE_HOST)}" new_variables = [] for variable_dic in testset["teststeps"][0]["variables"]: for variable_name, variable_str in variable_dic.items(): if isinstance(variable_str, str): parsed_variable_str = variable_string_add_param(variable_str) else: parsed_variable_str = variable_str new_variables.append({variable_name: parsed_variable_str}) testset["teststeps"][0]["variables"] = new_variables new_setup_hooks = [] for setup_hook_str in testset["teststeps"][0]["setup_hooks"]: # if setup_hook_str.startswith("${setup_fund_order_conf") and not setup_hook_str.endswith( # "||$DB_CONNECT)}"): # setup_hook_str = setup_hook_str[:-2] + "||$DB_CONNECT)}" # if setup_hook_str.startswith("${setup_disconf_operation_merge") and not setup_hook_str.endswith( # "||$DISCONF_HOST)}"): # setup_hook_str = setup_hook_str[:-2] + "||$DISCONF_HOST)}" parsed_setup_hook_str = setup_or_teardown_string_add_param(setup_hook_str, is_setup=True) new_setup_hooks.append(parsed_setup_hook_str) testset["teststeps"][0]["setup_hooks"] = new_setup_hooks new_teardown_hooks = [] for teardown_hook_str in testset["teststeps"][0]["teardown_hooks"]: # if teardown_hook_str.startswith("${teardown_disconf_operation_merge") and not teardown_hook_str.endswith( # "||$DISCONF_HOST)}"): # teardown_hook_str = teardown_hook_str[:-2] + "||$DISCONF_HOST)}" parsed_teardown_hook_str = setup_or_teardown_string_add_param(teardown_hook_str, is_setup=False) new_teardown_hooks.append(parsed_teardown_hook_str) testset["teststeps"][0]["teardown_hooks"] = new_teardown_hooks if "request_teardown_hooks" in testset["teststeps"][0]: new_request_teardown_hooks = [] for teardown_hook_str in testset["teststeps"][0]["request_teardown_hooks"]: parsed_teardown_hook_str = setup_or_teardown_string_add_param(teardown_hook_str, is_setup=False) new_request_teardown_hooks.append(parsed_teardown_hook_str) testset["teststeps"][0]["request_teardown_hooks"] = new_request_teardown_hooks if tm_obj: testset["teststeps"][0]["case_id"] = tm_obj.id testset["teststeps"][0]["sub_case_id"] = tc_obj.id testset["teststeps"][0]["is_main"] = True else: testset["teststeps"][0]["case_id"] = tc_obj.id testset["teststeps"][0]["is_main"] = False # # 注释合并请求报文 # intf_r_obj = aidrm.get_request(api_intf_id=tc_obj.api_intf_id) # if intf_r_obj and intf_r_obj.request: # default_body_base = json_loads(intf_r_obj.request) # default_body_detail = json_loads(intf_r_obj.request_detail) # # 根据intf_r_obj.request_detail中首层字段的必填性,把非必填字段去除 # for key_dic in default_body_detail: # if not key_dic["isRequired"]: # if isinstance(default_body_base, list): # default_body_base = [] # else: # default_body_base.pop(key_dic["paramName"]) # # if default_body_base: # request_type = tc_obj.request_type if tm_obj else tc_obj.type # # # 合并请求报文 # # http # if request_type == 1: # tc_body = testset["teststeps"][0]["request"]["json"] # testset["teststeps"][0]["request"]["json"] = merge_request_body(default_body_base, tc_body) # # dubbo # elif request_type == 2: # tc_body = testset["teststeps"][0]["request"]["json"]["args"] # testset["teststeps"][0]["request"]["json"]["args"] = merge_request_body( # default_body_base, tc_body, is_list=True # ) # # mq # elif request_type == 3: # tc_body_str = testset["teststeps"][0]["request"]["json"]["msg"] # tc_body = json_loads(tc_body_str) # testset["teststeps"][0]["request"]["msg"] = json_dumps( # merge_request_body(default_body_base, tc_body) # ) testset["teststeps"][0]["request"].pop("isMerge", None) # 增加支持form表单类型接口 # intf_obj = aiim.get_intf(id=tc_obj.api_intf_id) if intf_obj.intf_type == 'HTTP': intf_info_dic = json_loads(intf_obj.intf_info) headers_dic = json_loads(intf_info_dic["headers"].replace("'", "\"")) if 'Content-Type' in headers_dic: if 'application/x-www-form-urlencoded' in headers_dic['Content-Type']: testset["teststeps"][0]["request"]["data"] = testset["teststeps"][0]["request"]["json"] testset["teststeps"][0]["request"].pop("json") return testset
def save_task_results(self): """保存测试结果到任务运行结果表api_run_task_result和用例复用表api_testcase_reuse_record""" summary_list = [] for summary_path in self.summary_path_list: if not summary_path or 'worker_summary_path is None' == summary_path: continue with open(summary_path, 'r') as f: summary_str = f.readline() summary_dict = json_loads(summary_str) summary_list.append(summary_dict) if not self.run_task_result_id: self.run_task_result_id = summary_dict['run_task_result_id'] if 'run_task_result_id' in summary_dict else None if not self.log_dir: self.log_dir = summary_dict['log_dir'] if 'log_dir' in summary_dict else None # run_task_result_id = summary_list[0]['run_task_result_id'] # log_dir = summary_list[0]['log_dir'] # 保存summary_list到服务器文件目录run_task_logs with open('{0}task_run_{1}_summary.log'.format(self.log_dir, self.run_task_result_id), 'w') as f: f.write(json_dumps(summary_list)) callback_task_obj = CeleryTaskRecordManager.get_callback_celery(api_run_task_result_id=self.run_task_result_id) try: # 更新celery_task_record表的字段celery_task_status为RUNNING CeleryTaskRecordManager.update_celery(callback_task_obj.id, celery_task_status='RUNNING') total_cases = 0 for summary in summary_list: total_cases += summary.pop('total_cases') res_obj = ApiRunTaskResultManager.get_result(id=self.run_task_result_id) task_obj = ApiTaskInfoManager.get_task(id=res_obj.api_task_id) # 获取task全部的intf_id_list if task_obj.task_type in (1, 3): task_intf_id_list = json_loads(task_obj.case_tree)['intf_id_list'] else: task_intf_id_list = json_loads(task_obj.effect_intf_id_list) # 更新api_testcase_reuse_record表, 并获取covered_intf_id_set, run_cases, success_cases res_list = save_testcase_reuse_record(summary_list) # 本次测试包含的接口id集合 covered_intf_id_set = res_list[0] # 本次运行的用例数 run_cases = res_list[1] # 本次成功的用例数 success_cases = res_list[2] # 本次未覆盖的接口id列表 uncovered_intf_id_list = list(set(task_intf_id_list) ^ covered_intf_id_set) # 本次失败的用例数 fail_cases = run_cases - success_cases # 本次未运行的用例数 not_run_cases = total_cases - run_cases # 更新api_run_task_result表 ApiRunTaskResultManager.update_result( self.run_task_result_id, total_cases=total_cases, not_run_cases=not_run_cases, run_cases=run_cases, success_cases=success_cases, fail_cases=fail_cases, end_time=datetime.now(), covered_intf_id_list=json_dumps(list(covered_intf_id_set)), uncovered_intf_id_list=json_dumps(uncovered_intf_id_list) ) # 更新celery_task_record表的字段celery_task_status为SUCCESS CeleryTaskRecordManager.update_celery(callback_task_obj.id, celery_task_status='SUCCESS') except Exception as err: # 更新api_run_task_result表 ApiRunTaskResultManager.update_result(self.run_task_result_id, end_time=datetime.now()) # 更新celery_task_record表的字段celery_task_status为ERROR CeleryTaskRecordManager.update_celery(callback_task_obj.id, celery_task_status='ERROR') print('\n'.join([str(err), traceback.format_exc()])) raise Exception(err)
def get_all_pv_id_list(self, target_pv_id_list, testcase_id_list=None, testcase_main_id_list=None): if testcase_id_list: for testcase_id in testcase_id_list: obj = self.atim.get_testcase(id=testcase_id) include_list = json_loads(obj.include) public_variables_list = [] for include in include_list: if 'public_variables' in include: public_variables_list = include['public_variables'] # public_variables_list = include_list[0]['public_variables'] # setup_cases_list = include_list[1]['setup_cases'] for public_variable_id in public_variables_list: if public_variable_id not in target_pv_id_list: target_pv_id_list.append(public_variable_id) if obj.setup_case_list: setup_case_list = json_loads(obj.setup_case_list) for setup_case_str in setup_case_list: case_type, case_id, case_flow_id = parse_setup_case_str( setup_case_str) if case_type == 1: testcase_id_list = [case_id] target_pv_id_list = self.get_all_pv_id_list( target_pv_id_list, testcase_id_list=testcase_id_list) elif case_type == 2: testcase_main_id_list = [case_id] target_pv_id_list = self.get_all_pv_id_list( target_pv_id_list, testcase_main_id_list=testcase_main_id_list) elif testcase_main_id_list: exist_main_teardown_var_name = set() # 已加载的全链路独立后置中的公共变量名称集合 for testcase_id in testcase_main_id_list: tm_obj = self.atmm.get_testcase_main(id=testcase_id) sub_list = json_loads(tm_obj.sub_list) for sub_id in sub_list: ts_obj = self.atsm.get_testcase_sub(id=sub_id) include_list = json_loads(ts_obj.include) public_variables_list = include_list[0]['public_variables'] for public_variable_id in public_variables_list: if public_variable_id not in target_pv_id_list: target_pv_id_list.append(public_variable_id) # 处理全链路用例独立后置步骤中的公共变量 if tm_obj.main_teardown_hooks: variable_regexp = r"\$([\w_]+)" main_teardown_variables = re.findall( variable_regexp, str(tm_obj.main_teardown_hooks)) for target in main_teardown_variables: if target in exist_main_teardown_var_name: continue intf_id = ts_obj.api_intf_id intf_obj = ApiIntfInfoManager.get_intf(id=intf_id) system_id = intf_obj.api_system_id s_var_obj = ApiPublicVariableInfoManager.get_variable( variable_name=target, api_system_id=system_id) if s_var_obj: target_pv_id_list.append(s_var_obj.id) else: company_id = ApiSystemInfoManager.get_system( id=system_id).api_company_id c_var_obj = ApiPublicVariableInfoManager.get_variable( variable_name=target, api_company_id=company_id) if c_var_obj: target_pv_id_list.append(c_var_obj.id) exist_main_teardown_var_name.add(target) return target_pv_id_list
def _add_pv(self, testset, tc_obj, intf_obj, tm_obj=None): """ 添加公共变量到config """ # testset增加主用例用例独立后置中的公共变量 if not self.has_extract_variable_in_main_teardown and tm_obj and tm_obj.main_teardown_hooks: variable_regexp = r"\$([\w_]+)" # main_teardown_hooks = json_loads(tm_obj.main_teardown_hooks) main_teardown_variables = re.findall(variable_regexp, str(tm_obj.main_teardown_hooks)) for target in main_teardown_variables: system_id = intf_obj.api_system_id s_var_obj = ApiPublicVariableInfoManager.get_variable(variable_name=target, api_system_id=system_id) if s_var_obj: testset["config"]["variables"].append({ target: s_var_obj.value.split('##')[0] }) else: company_id = ApiSystemInfoManager.get_system(id=system_id).api_company_id c_var_obj = ApiPublicVariableInfoManager.get_variable( variable_name=target, api_company_id=company_id) if c_var_obj: testset["config"]["variables"].append({ target: c_var_obj.value.split('##')[0] }) self.has_extract_variable_in_main_teardown = True teststep = testset["teststeps"][0] include_list = json_loads(tc_obj.include) pv_list = None for include in include_list: if "public_variables" in include: pv_list = include["public_variables"] break """用例存在引用公共变量""" if pv_list: """存在公共变量临时修改""" if self.confirmed_pv_changes: for pv_change_dic in self.confirmed_pv_changes: if pv_change_dic['changedValue']: if pv_change_dic['type'] == 'files': continue # 暂不支持文件类型公共变量临时修改 elif pv_change_dic['type'] in ['constant', 'function', 'db']: if int(pv_change_dic['pvId']) in pv_list: pv_list.remove(int(pv_change_dic['pvId'])) testset["config"]["variables"].append({ pv_change_dic['name']: pv_change_dic['changedValue'] }) # if int(pv_change_dic['pvId']) in pv_list: # if pv_change_dic['type'] == 'files': # continue # 暂不支持文件类型公共变量临时修改 # pv_list.remove(int(pv_change_dic['pvId'])) # testset["config"]["variables"].append({ # pv_change_dic['name']: pv_change_dic['changedValue'] # }) pv_objs = avim.get_variables_in_id_list(pv_list) for obj in pv_objs: if obj.type == 'files': """特殊处理files类型的公共变量""" file_variable_name = obj.variable_name try: file_path_list = eval(obj.value) except (SyntaxError, NameError): file_path_list = None if not isinstance(file_path_list, list): continue target_key = None for key in teststep['request']['json']: if teststep['request']['json'][key] == '$' + file_variable_name: teststep['request']['files'] = [] for i in range(len(file_path_list)): if 'f_name' in teststep['request']['json']: file_name = teststep['request']['json']['f_name'][i] else: file_name = str(file_path_list[i]).split('/')[-1] try: teststep['request']['files'].append( (key, (file_name, open(file_path_list[i], "rb"), "multipart/form-data")) ) except FileNotFoundError: # 公共变量指定的文件不存在 pass target_key = key break if target_key: teststep['request']['json'].pop('f_name', 0) teststep['request']['json'].pop(target_key) teststep['request']['data'] = teststep['request']['json'] teststep['request'].pop('json') elif obj.type == 'function': """处理自定义方法类型的公共变量""" is_exist = False for exist_pv_dic in testset["config"]["variables"]: for key in exist_pv_dic: if key == obj.variable_name: is_exist = True if is_exist: break if not is_exist: variable_string = variable_string_add_param(str(obj.value)) testset["config"]["variables"].append({ obj.variable_name: variable_string }) elif obj.type == 'db': """处理db类型的公共变量""" is_exist = False for exist_pv_dic in testset["config"]["variables"]: for key in exist_pv_dic: if key == obj.variable_name: is_exist = True if is_exist: break if not is_exist: testset["config"]["variables"].append({ obj.variable_name: [v.strip() for v in str(obj.value).strip('##').split('##')][0] }) else: """处理key-value类型的公共变量""" is_exist = False for exist_pv_dic in testset["config"]["variables"]: for key in exist_pv_dic: if key == obj.variable_name: is_exist = True if is_exist: break if not is_exist: var_value = [v.strip() for v in str(obj.value).strip('##').split('##')][0] save_as = obj.save_as if obj.save_as else 'str' if save_as in ['num', 'bool', 'list', 'dict']: try: var_value = eval(var_value) except SyntaxError: var_value = var_value testset["config"]["variables"].append({ obj.variable_name: var_value }) return testset
def update_task_info(self, api_task_id, changes, seq_no): """ 解析回调接口返回的变更内容 :param api_task_id: :param changes: :param seq_no: :return: """ affect_http = set() affect_dubbo = set() affect_mq = set() affect_elasticJob = set() for affects in [change['affects'] for change in changes]: for methods in [affect['methods'] for affect in affects]: for method in methods: if method.get('http'): method_http = method.get('http').get('url') affect_http.add(method_http) if method.get('dubbo'): role = method.get('dubbo').get('role') method_dubbo = method.get('dubbo').get('service').get('interface') + '.' + method.get('method') \ if role == 'service' else method.get('dubbo').get('reference').get( 'interface') + '.' + method.get('method') affect_dubbo.add(method_dubbo) if method.get('mq'): role = method.get('mq').get('role') method_mq = method.get('mq').get('producer').get('topic') \ if role == 'producer' else method.get('mq').get('consumer').get( 'topic') + '.' + method.get('mq').get('consumer').get( 'tag') affect_mq.add(method_mq) if method.get('elasticJob'): method_elasticJob = method.get('elasticJob').get('class') + '.' + method.get('method') affect_elasticJob.add(method_elasticJob) affects = { "http": list(affect_http), "dubbo": list(affect_dubbo), "mq": list(affect_mq), "elasticJob": list(affect_elasticJob)} # 查询atp平台是否存在对应接口,如有则将接口id填入effect_intf_id_list,如无则将接口数据填入uncovered_info task_info = self.atim.get_task(id=api_task_id) effect_intf_id_list = set(json_loads(task_info.effect_intf_id_list)) if task_info.effect_intf_id_list else set() uncovered_info = json_loads(task_info.uncovered_info) if task_info.uncovered_info else {} uncovered_info_http = set(uncovered_info.get('http')) if uncovered_info.get('http') else set() uncovered_info_dubbo = set(uncovered_info.get('dubbo')) if uncovered_info.get('dubbo') else set() uncovered_info_mq = set(uncovered_info.get('mq')) if uncovered_info.get('mq') else set() uncovered_info_elasticJob = set(uncovered_info.get('elasticJob')) if uncovered_info.get('elasticJob') else set() for intf_name in affects['http']: if intf_name: intf_info = self.aiif.get_intf(intf_name=intf_name) if intf_info: testcase_info = self.atcm.get_testcase(api_intf_id=intf_info.id) if testcase_info: affects['http'] = affects['http'].remove(intf_name) effect_intf_id_list.add(intf_info.id) else: uncovered_info_http.add(intf_name) else: uncovered_info_http.add(intf_name) for intf_name in affects['dubbo']: if intf_name: intf_info = self.aiif.get_intf(intf_name=intf_name) if intf_info: testcase_info = self.atcm.get_testcase(api_intf_id=intf_info.id) if testcase_info: affects['dubbo'] = affects['dubbo'].remove(intf_name) effect_intf_id_list.add(intf_info.id) else: uncovered_info_dubbo.add(intf_name) else: uncovered_info_dubbo.add(intf_name) for intf_name in affects['mq']: if intf_name: intf_info = self.aiif.get_intf(intf_name=intf_name) if intf_info: testcase_info = self.atcm.get_testcase(api_intf_id=intf_info.id) if testcase_info: affects['mq'] = affects['mq'].remove(intf_name) effect_intf_id_list.add(intf_info.id) else: uncovered_info_mq.add(intf_name) else: uncovered_info_mq.add(intf_name) for intf_name in affects['elasticJob']: if intf_name: intf_info = self.aiif.get_intf(intf_name=intf_name) if intf_info: testcase_info = self.atcm.get_testcase(api_intf_id=intf_info.id) if testcase_info: affects['elasticJob'] = affects['elasticJob'].remove(intf_name) effect_intf_id_list.add(intf_info.id) else: uncovered_info_elasticJob.add(intf_name) else: uncovered_info_elasticJob.add(intf_name) effect_intf_id_list = list(effect_intf_id_list) uncovered_info['http'] = list(uncovered_info_http) uncovered_info['dubbo'] = list(uncovered_info_dubbo) uncovered_info['mq'] = list(uncovered_info_mq) uncovered_info['elasticJob'] = list(uncovered_info_elasticJob) # 判断当前是否是同一个api_task下的最后一个回调,如果是则更新api_task_info表里面对应记录的task_status为1(启动) git_diffs = self.gdvm.get_git_diff_versions_special(seq_no, api_task_id) if not git_diffs: self.atim.update_task(api_task_id, effect_intf_id_list=json_dumps(effect_intf_id_list), uncovered_info=json_dumps(uncovered_info), task_status=1) else: flag = 0 for row in git_diffs: if not row.detail: flag = 1 break if flag == 1: self.atim.update_task(api_task_id, effect_intf_id_list=json_dumps(effect_intf_id_list), uncovered_info=json_dumps(uncovered_info)) if flag == 0: self.atim.update_task(api_task_id, effect_intf_id_list=json_dumps(effect_intf_id_list), uncovered_info=json_dumps(uncovered_info), task_status=1)
def celery_run_debug(**kwargs): # with app.app_context(): if True: log_dir = kwargs.get('log_dir') report_id = kwargs.pop('report_id', None) plan_name = kwargs.pop('plan_name', None) project_id = kwargs.pop('project_id', None) testcase_main_id_list = kwargs.get('testcase_main_id_list', None) if testcase_main_id_list: is_main = True else: is_main = False print('=============================={dir}run_{report_id}.log'.format( dir=log_dir, report_id=report_id)) hr_kwargs = { "failfast": True, "log_path": '{dir}run_{report_id}.log'.format(dir=log_dir, report_id=report_id) } runner = HttpRunner(**hr_kwargs) try: loader = ApiTestLoader(**kwargs) testset = loader.get_testset_list() test_meta_list = loader.get_test_meta_list() if not testset: raise LoadCaseError('没有可执行的用例') # logger.debug("{1} testset:{0}".format(testset, type(testset))) except Exception as err: save_report(report_path=None, runner_summary=None, project_id=project_id, report_id=report_id) hr_logger.log_error("【ERROR】组装用例出错!") hr_logger.log_error('\n'.join([str(err), traceback.format_exc()])) hr_logger.log_info("【END】测试结束!") hr_logger.remove_handler(runner.handler) raise LoadCaseError try: hr_logger.log_info("【START】测试开始! (ง •_•)ง") hr_logger.log_info("【环境】: {}".format(kwargs.get('env_name', None))) # time.sleep(3) try: testset_json = json_dumps(testset) except Exception: testset_json = testset hr_logger.log_info("【调用HttpRunner】: {0}".format(testset_json)) runner.run(testset) hr_logger.log_info("【结束调用HttpRunner】") # raise RunCaseError perfect_summary(runner.summary, test_meta_list) """记录用例复用记录""" api_runner.summary_remove_file_obj(runner.summary) summary_for_reuse = copy.deepcopy(runner.summary) # print(json_dumps(summary_for_reuse)) summary_for_reuse = api_runner.add_memo(summary_for_reuse) # 识别错误 summary_for_reuse = api_runner.identify_errors(summary_for_reuse) # 更新api_testcase_reuse_record表, 并获取covered_intf_id_set, run_cases, success_cases save_testcase_reuse_record([{ "summary": json_loads(json_dumps(summary_for_reuse)) }]) del summary_for_reuse # hr_logger.log_info("【runner.summary】: {}".format(runner.summary)) '''报告优化:1、汉化(包括日志里面的字段) 2、开始时间和持续时间合并成一行 3、增加一个字段“错误类型”,如果用例错误,显示该字段,并说明期望与预期值; 否则该字段不显示 4.log里面去掉一些数据重复和不重要的;行和字段(请求headers,返回体的headers,reason,url,“”ok”) 5.将请求体和返回值数据缩进,且字典里面的key颜色加粗 6.新增接口请求类型字段,http、dubbo、mq''' from atp.utils.custom import json_contains, db_validate, db_json_validate for detail in runner.summary["details"]: for record in detail["records"]: '''增加用例类型:test_meta_list["intf_type"]''' record["intf_type"] = test_meta_list[0]["intf_type"] '''删除报告一些无需关注的字段''' request_keys = ["json", "start_timestamp"] response_keys = [ "elapsed_ms", "encoding", 'ok', 'url', 'reason', 'cookies' ] for request_key in request_keys: if request_key in record["meta_data"]["request"]: del record["meta_data"]["request"][request_key] for respones_key in response_keys: if respones_key in record["meta_data"]["response"]: del record["meta_data"]["response"][respones_key] '''record.status出现error, 抛出错误信息''' if record['status'] == 'error': error_msg = record['attachment'] raise Exception(error_msg) '''报告增加一列:错误类型:''' for validate in record["meta_data"]["validators"]: if validate["comparator"] == "json_contains": check_value = validate["check_value"] expect_value = validate["expect"] if json_contains(check_value, expect_value) is not True: validate["check_result"] = "fail" record["status"] = "failure" detail["stat"]["failures"] += 1 detail["stat"]["successes"] -= 1 runner.summary["stat"]["failures"] += 1 runner.summary["stat"]["successes"] -= 1 error_log = ( "预期:{}未在返回报文内".format(expect_value)) validate["error_log"] = { "json_contains": error_log } elif validate["comparator"] == "db_validate": check_value = validate["check_value"] expect_value = validate["expect"] if db_validate(check_value, expect_value) is not True: validate["check_result"] = "fail" record["status"] = "failure" detail["stat"]["failures"] += 1 detail["stat"]["successes"] -= 1 runner.summary["stat"]["failures"] += 1 runner.summary["stat"]["successes"] -= 1 error_log = ("预期:{0},实际是:{1}".format( expect_value, check_value)) validate["error_log"] = { "db_validate": error_log } elif validate["comparator"] == "db_json_validate": check_value = validate["check_value"] expect_value = validate["expect"] if not db_json_validate(check_value, expect_value): validate["check_result"] = "fail" record["status"] = "failure" detail["stat"]["failures"] += 1 detail["stat"]["successes"] -= 1 runner.summary["stat"]["failures"] += 1 runner.summary["stat"]["successes"] -= 1 error_log = ("预期:{0},实际是:{1}".format( expect_value, json.dumps(check_value).encode( 'utf-8').decode('unicode_escape'))) validate["error_log"] = { "db_json_validate": error_log } hr_logger.log_info("【runner.summary】: {}".format(runner.summary)) runner_summary = copy.deepcopy(runner.summary) """把每条用例执行成功与否记录到testcase_info.last_run""" try: save_last_run(runner_summary, is_main=is_main) except Exception as e: print('\n'.join([str(e), traceback.format_exc()])) # hr_logger.log_error("【ERROR】运行用例出错!") # hr_logger.log_error('\n'.join([str(e), traceback.format_exc()])) # print("runner_summary_list{}".format(runner.summary)) # report_path = runner.gen_html_report( # html_report_name=plan_name if plan_name else 'default', # html_report_template=config.REPORT_TEMPLATE_PATH, # html_report_dir=config.REPORT_DIR # ) # print('report_path:{}'.format(report_path)) # report_path = report_path.split('reports')[1] # report_url = get_host() + r':8899/reports' + report_path # # print('AC report_path:{}'.format(report_path)) report_url = '不生成报告' save_report(report_url, runner_summary, project_id, report_id=report_id, is_main=is_main) except Exception as err: save_report(report_path=None, runner_summary=runner.summary, project_id=project_id, report_id=report_id) hr_logger.log_error("【ERROR】运行用例出错!") hr_logger.log_error('\n'.join([str(err), traceback.format_exc()])) time.sleep(1) raise RunCaseError finally: time.sleep(1) hr_logger.log_info("【END】测试结束!") hr_logger.remove_handler(runner.handler) return report_url
def run_task(self, task_id): """运行回归测试任务""" # 获取回归测试任务详细内容 self.get_task_obj(task_id) # celery_no_list = [] # total_cases = 0 # 获取待测试的接口id列表 to_run_intf_id_list = [] product_line_id_list = [] if self.task_obj.task_type in (1, 3): to_run_dic = json_loads(self.task_obj.case_tree) to_run_intf_id_list = to_run_dic.get('intf_id_list', []) # to_run_testcase_main_id_list = to_run_dic.get('testcase_main_id_list', []) product_line_id_list = to_run_dic.get('product_line_id_list', []) elif self.task_obj.task_type == 2: to_run_intf_id_list = json_loads(self.task_obj.effect_intf_id_list) # 待测试列表为空 if not isinstance(to_run_intf_id_list, list) or not isinstance(product_line_id_list, list) or \ (not to_run_intf_id_list and not product_line_id_list): # if self.run_task_result_id: # ApiRunTaskResultManager.delete_result(self.run_task_result_id) return None # 接口用例智能去重 intf_testcase_map = {} for intf_id in to_run_intf_id_list: testcase_id_list = self.get_testcase_id_list_filter_by_tag(intf_id=intf_id) if testcase_id_list: intf_testcase_map[intf_id] = testcase_id_list if not intf_testcase_map: # 如果没有可运行的用例,直接返回 return None filtered_intf_testcases_map = smart_filter_testcase(intf_testcase_map, self.table_data) # 写入回归测试任务记录表 self.set_run_task_result(task_id) # 拆分测试任务 header = [] countdown = self.reboot_sleep if self.reboot_sleep else 1 print(countdown) # 拆分全链路测试用例 if self.run_main_case_in_parallel: # 每条全链路用例分配一个worker timed_to_run_product_line_id_list = [] for _ in range(self.times): # 循环次数 timed_to_run_product_line_id_list.extend(product_line_id_list) for product_line_id in timed_to_run_product_line_id_list: testcase_main_id_list = self.get_testcase_id_list_filter_by_tag(product_line_id=product_line_id) for testcase_main_id in testcase_main_id_list: header.append( celery_run_single_intf_or_single_main_case.signature( (self.run_task_result_id, self.env_id, testcase_main_id, False, self.run_task_log_dir), countdown=countdown ) ) countdown = self.get_next_countdown(countdown) print(countdown) else: # 每个最末产品线分配一个worker for product_line_id in product_line_id_list: testcase_main_id_list = self.get_testcase_id_list_filter_by_tag(product_line_id=product_line_id) if testcase_main_id_list: timed_testcase_main_id_list = [] for _ in range(self.times): # 循环次数 timed_testcase_main_id_list.extend(testcase_main_id_list) header.append( celery_run_main_case_list.signature( (self.run_task_result_id, self.env_id, timed_testcase_main_id_list, self.run_task_log_dir), countdown=countdown ) ) countdown = self.get_next_countdown(countdown) print(countdown) # 拆分接口测试用例 -- 每个接口分配一个worker for intf_id, testcase_id_list in filtered_intf_testcases_map.items(): if testcase_id_list: timed_testcase_id_list = [] for _ in range(self.times): # 循环次数 timed_testcase_id_list.extend(testcase_id_list) header.append( celery_run_single_intf_or_single_main_case.signature( (self.run_task_result_id, self.env_id, intf_id, True, self.run_task_log_dir, timed_testcase_id_list), countdown=countdown ) ) countdown = self.get_next_countdown(countdown) print(countdown) # worker_num = len(header) # logger.info('本次运行总共需要的worker数量: {}'.format(worker_num)) # # time.sleep(3) # # 分配异步测试任务, 增加回调任务收集测试结果 # callback = celery_collect_results.s() # chord_result = chord(header)(callback) # callback_celery_no = chord_result.id # # # 更新api_run_task_result表,本次运行总共需要的worker数量 # ApiRunTaskResultManager.update_result(id_=self.run_task_result_id, worker_num=worker_num) # # 写入celery任务表 # CeleryTaskRecordManager.insert_celery(celery_task_no=callback_celery_no, celery_task_status='WAITING', # api_run_task_result_id=self.run_task_result_id) # # return callback_celery_no # 分配异步测试任务, 增加回调任务收集测试结果 callback_celery_no = self._assign_celery_tasks(header) return callback_celery_no