def setUp(self): """ 测试用例前置条件 :return: """ self.L = Log("MyTestClass").logger self.L.critical("测试准备") self.runner = Runner() self.case_path = os.path.abspath(".") + "/testCase/"
class KeyGlobal(object): """ 1.RECORD 关键字,将指定的值,存储为全局变量,在任意test case均可直接调用 2.READ 关键字,读取存储的全局变量 数据存放到 data_global{} 中 """ def __init__(self): global global_dict self.L = Log("KeyGlobal").logger def set_global_key(self, key, value): """ 实现RECORD,将value,存储为key的值,全局可用data_global :param key: :param value: :return: """ global_dict[key] = value self.L.info("{%s: %s}已设置" % (key, value)) def get_global_value_by_key(self, key: str, key_type: str = ""): """ 给REQUEST\LREQUEST\RESPONSE\LRESPONSE 提供检查方法 :param key_type: :param key: :return: """ try: if key_type == "REQUEST": value = UniversalDataOperation().get_dicts_value_by_key_path( global_dict["data_current"]["req"], key) elif key_type == "LREQUEST": value = UniversalDataOperation().get_dicts_value_by_key_path( global_dict["data_last"]["req"], key) elif key_type == "RESPONSE": value = UniversalDataOperation().get_dicts_value_by_key_path( global_dict["data_current"]["resp"], key) elif key_type == "LRESPONSE": value = UniversalDataOperation().get_dicts_value_by_key_path( global_dict["data_last"]["resp"], key) else: value = UniversalDataOperation().get_dicts_value_by_key_path( global_dict, key) return value except KeyError: raise Exception("键名异常: %s" % key)
class TestMain(unittest.TestCase): def setUp(self): """ 测试用例前置条件 :return: """ self.L = Log("MyTestClass").logger self.L.critical("测试准备") self.runner = Runner() self.case_path = os.path.abspath(".") + "/testCase/" def test_user_login(self): """ 测试用户登录 :return: """ case_path = self.case_path + "user.yaml" self.L.info("开始执行测试用例:%s" % case_path) self.runner.do_request(case_path) def test_user_logout(self): """ 测试用户注销 :return: """ case_path = self.case_path + "user.yaml" self.L.info("开始执行测试用例:%s" % case_path) self.runner.do_request(case_path) def test_customer(self): """ 测试苗叔向基地购买流程 :return: """ case_path = self.case_path + "苗叔向基地购买流程.yaml" self.L.info("开始执行测试用例:%s" % case_path) self.runner.do_request(case_path) def test_user_shop(self): """ 测试基地库存 :return: """ case_path = self.case_path + "Shopbuy.yaml" self.L.info("开始执行测试用例:%s" % case_path) self.runner.do_request(case_path)
class KeyRequest(object): """ 1.解析data_file中关键字REQUEST、GREQUEST、LREQUEST、IMAGE的用法,并执行请求,将请求结果按str类型返回给调用方 2.REQUEST() 关键字,返回当前请求中参数值,如REQUEST(id),则返回当次请求中id的值 在data_current中获取 3.LREQUEST() 关键字,返回上一次请求中参数值,如LREQUEST(id),则返回上次请求中id的值 在data_last中获取 4.IMAGE() 关键字,参数是图片路径,用户请求中,说明该接口参数是图片信息 """ def __init__(self): self.L = Log("KeyRquest").logger self.kg = KeyGlobal() def get_current_request_info_by_argument( self, field_name: str) -> str or dict or list: """ 解析关键字REQUEST() :param field_name: 当前请求中那个参数的值 :return: """ try: self.L.info("解析关键字REQUEST,解析路径为: %s" % field_name) result = self.kg.get_global_value_by_key(field_name, "REQUEST") self.L.debug("解析REQUEST(%s)的结果是:%s" % (field_name, result)) return result except Exception as e: raise Exception("解析关键字REQUEST数据异常,异常信息:%s" % e) def get_last_request_info_by_argument( self, field_name: str) -> str or dict or list: """ 解析关键字LREQUEST() :param field_name: 上次请求中那个参数的值 :return: """ try: self.L.info("解析关键字LREQUEST,解析路径为: %s" % field_name) result = self.kg.get_global_value_by_key(field_name, "LREQUEST") self.L.debug("解析LREQUEST(%s)的结果是:%s" % (field_name, result)) return result except Exception as e: raise Exception("解析关键字LREQUEST数据异常,异常信息:%s" % e) def get_image_info(self, image_name: str) -> str: """ 解析关键字IMAGE() :param image_name: 图片详细路径或名字 :return: """ try: return image_name except Exception as e: raise Exception("解析关键字IMAGE数据异常,异常信息:%s" % e)
class KeySquery(object): """ SQUERY(sql) 执行sql,将数据结果以list形式返回,如果list长度只有一个,则直接返回值 """ def __init__(self): self.L = Log("KeySquery").logger def get_sql_result(self, sql: str) -> str or list or int or float: """ SQUERY(sql) 执行sql,将数据结果以list形式返回,若只有一个元素则直接返回一个元素 :param sql: :return: """ try: # 执行SQL self.L.info("准备执行SQL:%s" % sql) cursor = self.__connect_database().cursor() cursor.execute(sql) result_tmp = cursor.fetchall() self.L.info("SQL执行结果 %s" % result_tmp) # 将返回的SQL结果序列成一维list result = [] for x in result_tmp: for y in list(x.values()): result.append(y) # 多个数据结果以list形式返回,一个元素则直接返回一个元素 if len(result) < 2: return result[0] else: return result except Exception as e: raise Exception("执行SQL异常 %s" % e) def __connect_database(self): """ 链接数据库 """ try: kg = KeyGlobal() config = kg.get_global_value_by_key("baseInfo.dataBase") self.L.debug("host: %s, database: %s" % (config["host"], config["database"])) db = pymysql.connect(host=config["host"], user=config["user"], port=config["port"], password=config["password"], database=config["database"], cursorclass=pymysql.cursors.DictCursor) return db except ConnectionError as e: raise ConnectionError("连接数据库错误 %s" % e)
class KeyResponse(object): """ RESPONSE(路径) 读取响应数据data_current 中的RESPONSE值字段,并将值取出返回str LRESPONSE() 关键字,返回上一次请求data_last中参数值str,如LREQUEST(id),则返回上次请求中id的值 特殊的,当入参中包含迭代器时,需要迭代查询结果,并按list形式返回,迭代标识符为 __iterations__ ,如传入的参数为key[__iterations__]["status"],返回key下每个模块中的status """ def __init__(self): self.L = Log("KeyResponse").logger self.li = [] self.kg = KeyGlobal() # self.global_data = json.loads('{"name":"中国","province":[{"name":"黑龙江","cities":{"city":["哈尔滨","大庆"]}},{"name":"广东","cities":{"city":["广州","深圳","珠海"]}},{"name":"台湾","cities":{"city":["台北","高雄"]}},{"name":"新疆","cities":{"city":["乌鲁木齐"]}}]}') def get_current_response_message(self, field_name: str) -> str or dict or list: """ 解析关键字RESPONSE() :param field_name: 当前请求中那个参数的值 :return: """ try: self.L.info("解析关键字RESPONSE,解析路径为: %s" % field_name) result = self.kg.get_global_value_by_key(field_name, "RESPONSE") self.L.debug("解析RESPONSE(%s)的结果是:%s" % (field_name, self.li)) return result except Exception as e: raise Exception("解析关键字RESPONSE数据异常,异常信息:%s" % e) def get_last_response_message(self, field_name: str) -> str or dict or list: """ 解析关键字LRESPONSE() LRESPONSE() 返回接口响应中的,key(json中的数据路径),下的数据 """ try: self.L.info("解析关键字LRESPONSE,解析路径为: %s" % field_name) result = self.kg.get_global_value_by_key(field_name, "LRESPONSE") self.L.debug("解析LRESPONSE(%s)的结果是:%s" % (field_name, self.li)) print(result) except Exception as e: raise Exception("解析关键字LRESPONSE数据异常,异常信息:%s" % e)
def __init__(self): self.L = Log("KeyRquest").logger self.kg = KeyGlobal()
def __init__(self): """ 请求类初始化本类日志 """ self.L = Log("SendRequest").logger
class UniversalDataOperation(object): def __init__(self): self.iterative_values = False self.L = Log("KeyGeneralMethods").logger def get_dicts_value_by_key_path(self, dicts: dict, key_path: str) -> str or list: """ 字典路径下的数据获取 :param dicts: 需要解析的源数据 :param key_path: 解析的路径,用"."号隔开,如:content.pn :return: 返回解析结果 :return: str or list """ # 将传入的字符串格式化为list key_path_r = key_path.split(".") # 调用私有方法进行解析 self.iterative_values = "__iterations__" in key_path_r result = self.__get_dicts_value_by_key_path(dicts, key_path_r) # 若返回到是字符串直接返回,若返回的是list且是一个元素返回字符串,若返回的是list且是多个元素返回一维list if isinstance(result, list): if len(result) == 1: self.L.debug("通过路径%s,查找到的数据是%s" % (key_path, result[0])) return result[0] else: self.L.debug("通过路径%s,查找到的数据是%s" % (key_path, result)) return result else: self.L.debug("通过路径%s,查找到的数据是%s" % (key_path, result)) return result def __get_dicts_value_by_key_path(self, dicts: dict or list, key_path: list) -> str or list: """ 实现字典值索引 :param dicts: 需要解析的源数据 :param key_path: 解析的路径,按list形式传入 :return: 返回解析结果 """ try: # 获取传入的字符串中的第一个key, i = key_path[0] # 判断当前是否为查找路径终点,若为终点,则直接取之返回。 否则继续往后查找字符串 if len(key_path) > 1: # 若查找的路径不是叶子节点,则删除当前节点 del key_path[key_path.index(i)] # 传入的参数是关键字"__iterations__"时,直接使用当前传入的字典 if "__iterations__" == i: return self.__get_dicts_value_by_key_path(dicts, key_path) # 传入的参数是数字时,从当前路径中,取出指定id下的值 elif i.isdigit(): return self.__get_dicts_value_by_key_path(dicts[int(i)], key_path) # 当传入的是一个字符串时,取出对应key下的value else: # 取值时,若是在list中,需要到list中去取对应key的值 if isinstance(dicts, list): temp_list = [] for x in dicts: temp_list.append(x[i]) return self.__get_dicts_value_by_key_path(temp_list, key_path) # 取值时,若是在dict中,直接取对应key的值 else: return self.__get_dicts_value_by_key_path(dicts[i], key_path) # 判断当前查找是否为叶子节点,并返回最终叶子节点值 else: # 叶子节点传入的参数是关键字"__iterations__"时,直接使用当前传入的字典 if "__iterations__" == i: # 叶子节点 if isinstance(dicts, list): return self.__list_flatten(dicts) else: return dicts # return dicts[i] # 叶子节点传入的参数是数字时,返回指定节点值 elif i.isdigit(): # 在叶子节点前,若出现过关键字"__iterations__"时,是返回每个节点下的第一个值 if self.iterative_values: tmp = [] for x in dicts: # 检查传入的值是否超过list下标,若超过不做处理 if len(x) > int(i): # 若传入的是list,直接取出对应id的值 if isinstance(x, list): tmp.append(x[int(i)]) # 若传入的是字符串,直接取出对应的值 else: tmp.append(x) return tmp # 在叶子节点前,若没出现过关键字"__iterations__"时,直接返回指定的值 else: return dicts[int(i)] # 叶子节点传入的参数是字符串时,返回指定节点值 else: # 取值时,若是在list中,需要到list中去取对应key的值 if isinstance(dicts, list): temp_list = [] for x in dicts: temp_list.append(x[i]) return temp_list # 取值时,若是在字典中,直接取出对应值 else: return dicts[i] except Exception as e: raise Exception("\n在内存数据:\n%s中\n未找到键:%s\nKeyError:%s" % (dicts, key_path, e)) def __list_flatten(self, list_src: list) -> list: """ 用于多层嵌套list解包,还原到1层list :param list_src: :return: """ tmp = [] for i in list_src: if type(i) is not list: tmp.append(i) else: tmp.extend(self.__list_flatten(i)) return tmp
class ProduceCaseYaml(object): """ 根据服务配置, 自动生在/testCase/templates下生成Case的Yaml模板 """ def __init__(self): self.L = Log("ProduceCaseYaml").logger def get_paths_yaml(self, file_name: str, host_name: str) -> None: """ 通过Swagger V2获取api文档 :param file_name: 想要存储yaml模板文件的文件名 :param host_name: 各服务域名 :return: 获取文档方法无需返回 """ json_content = SendRequest().get(str(host_name) + '/v2/api-docs') # 获取所有的paths节点 paths = json.loads(json_content)["paths"] path_detail_list = [] for p in paths: para_desc_list = [] try: desc = paths[p]["post"].get("tags", [])[0] para_desc_list.append(desc) # L.logger.debug("{'%s': '%s'}" % (p, desc)) paras = paths[p]["post"].get("parameters", []) except KeyError: desc = paths[p]["get"].get("tags", [])[0] para_desc_list.append(desc) # L.logger.debug("{'%s': '%s'}" % (p, desc)) paras = paths[p]["get"].get("parameters", []) p_dict = {} if paras is None: pass else: for para in paras: p_dict[para['name']] = u"%s_%s_%s" % ( para.get('type', 'noType'), para.get('required', 'noRequired'), para.get('description', 'noDescription')) para_desc_list.append(p_dict) path_detail_list.append({p: para_desc_list}) # demand_list = sorted(path_detail_list) now = time.strftime("_%Y-%m-%d_%H%M%S", time.localtime()) # L.logger.debug(path_detail_list) current_path = os.path.dirname(os.path.abspath(__file__)) if not os.path.exists(current_path + "/templates"): os.makedirs(current_path + "/templates") with codecs.open('./templates/' + str(file_name) + now + '.yaml', 'a', 'utf-8') as f: for item in path_detail_list: for k, v in item.items(): if isinstance(v[1], dict): f.write('\n%s:' % k) f.write(''' baseInfo: author: time: 20180931 logInfo: Debug directExecution: True generatePython: False requestMethod: POST requestHeaders: {'Host':'www.super-ping.com','Connection':'keep-alive','Cache-Control':'max-age=0','Accept':'text/html,*/*;q=0.01','X-Requested-With':'XMLHttpRequest','User-Agent':'Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/41.0.2272.89Safari/537.36','DNT':'1','Referer':'http://www.super-ping.com/?ping=www.google.com&locale=sc','Accept-Encoding':'gzip,deflate,sdch','Accept-Language':'zh-CN,zh;q=0.8,ja;q=0.6'} serverAddress: %s dataBase: host: 39.104.28.40 port: 3306 user: root password: YYJNo$QsaaSjgb8U3JoigB database: %s interfaceParameters: - ''' % (host_name, file_name.replace("_", "-").lower())) for x, y in v[1].items(): f.write(' %s:\n ' % x) f.write('interfaceAsserts:\n -\n') def add_latest_yaml(self): for key, host in hosts.items(): try: self.get_paths_yaml(key, host) except Exception as e: self.L.error("错误信息: %s" % e)
class Dispatcher(object): def __init__(self): """ 解析参数中的key或value信息,并返回各自关键字执行结果 """ self.L = Log("Dispatcher").logger def key_dispatcher(self, t_key_string: object, record_value="") -> object: """ 此为本类函数入口点, 将入参字符串转为 元组 待递归解析 :param t_key_string: 解析由Runner.py传入的字符串 :param record_value: 默认为空, 仅适用于RECORD关键字字典存值使用 :return: 返回 递归解析并调用路由后 的结果字符串 """ if isinstance(t_key_string, str) is False: return t_key_string else: return self.__key_dispatcher_realize((t_key_string, ), record_value) def __key_dispatcher_realize(self, t_key: tuple, record_value: str = "") -> str: """ 解析由key_dispatcher传入的 (待解析字符串, ) 元组, 递归解析字符串路中的关键字, 路由匹配调用各关键字类(如: KeyRequest, KeySquery)返回调用后结果字符串 :param t_key: 由runner调用时传入的字符串key转为的(key, )元组 :param record_value: 默认为空, 仅适用于RECORD关键字字典存值使用 :return: 若还可递归, 继续以 (递归函数本身, 关键字, 参数) 的元组 为入参 调用函数本身; 若无法递归 则返回字符串 """ # 所有传入字符串 以 左括号 做字符串切片 以解析 关键字 和 入参 if t_key[0].find('(') == -1: # 最后的字符串已不包含左括号 return t_key[0] else: # 以第一个左括号位置切片 解析 关键字 和 入参 left_str = (t_key[0][:t_key[0].find('(')]) # 去掉 待解析字符串中的 首尾括号; 否则 下次左括号位置为0时, 会导致解析错误 right_str = (t_key[0][t_key[0].find('(')+1:-1]) self.L.info("关键字: %s; 参数: %s" % (left_str, right_str)) return self.__key_route((left_str, self.__key_dispatcher_realize((right_str, left_str))), record_value) def __key_mapper(self, check_str: str, except_key: str) -> bool: """ 解析由递归函数传入字符串check_str中是否含有期望的关键字except_key, 如REQUEST, LREQUEST, RESPONSE等 :param check_str: 由递归函数传入字符串 :param except_key: 期望的关键字except_key, 如REQUEST, LREQUEST, RESPONSE等 :return: 匹配则返回True, 反之则反 """ result = check_str.split(" ")[-1:][0] == except_key self.L.debug("%s == %s -> %s" % (check_str, except_key, result)) return result def __key_route(self, key_tuple: tuple, record_value: str = "") -> str: """ 解析由key_dispatcher函数传入的(key_dispatcher对象, 关键字, 参数)的元组, 匹配调用各关键字类(如: Request(content.id))返回调用各关键字类的结果字符串 :param key_tuple: 由key_dispatcher函数传入的(key_dispatcher对象, 关键字, 参数)的元组 :param record_value: 默认为空, 仅适用于RECORD关键字字典存值使用 :return: 调用各关键字类的结果字符串 """ # todo: 仅与KeyRequest完成联调, 其他类待联调. 递归最后一层不出日志(情况未知), 尚无其他副作用 self.L.debug("left: %s | right: %s" % (key_tuple[0], key_tuple[1])) if self.__key_mapper(key_tuple[0], "REQUEST"): self.L.info("REQUEST is CALLED") kr = KeyRequest() return kr.get_current_request_info_by_argument(key_tuple[1]) elif self.__key_mapper(key_tuple[0], "LREQUEST"): self.L.info("LREQUEST is CALLED") kr = KeyRequest() return kr.get_last_request_info_by_argument(key_tuple[1]) elif self.__key_mapper(key_tuple[0], "RESPONSE"): self.L.info("RESPONSE is CALLED") kres = KeyResponse() return kres.get_current_response_message(key_tuple[1]) elif self.__key_mapper(key_tuple[0], "LRESPONSE"): self.L.info("LRESPONSE is CALLED") kres = KeyResponse() return kres.get_last_response_message(key_tuple[1]) elif self.__key_mapper(key_tuple[0], "RECORD"): self.L.info("RECORD is CALLED") kg = KeyGlobal() return kg.set_global_key(key_tuple[1], record_value) elif self.__key_mapper(key_tuple[0], "READ"): self.L.info("READ is CALLED") kg = KeyGlobal() return kg.get_global_value_by_key(key_tuple[1]) elif self.__key_mapper(key_tuple[0], "SQUERY"): self.L.info("SQUERY is CALLED") sq = KeySquery() return sq.get_sql_result(key_tuple[1]) elif self.__key_mapper(key_tuple[0], "FAKER"): self.L.info("FAKER is CALLED") kf = KeyFaker() return kf.get_some_things(key_tuple[1]) elif self.__key_mapper(key_tuple[0], "REGEX"): self.L.info("REGEX is CALLED") # return "REGEX" else: raise Exception('非法关键字: %s' '\n合法的关键字:' '\nREQUEST 用于立即发送网络请求, ' '\nLREQUEST 用于获取上次请求, ' '\nRESPONSE 用于立即获取网络响应, ' '\nLRESPONSE 用于获取上次网络响应, ' '\nSQUERY 用于查询数据库, ' '\nRECORD 用于存储全局缓存, ' '\nREAD 用于读取全局缓存, ' '\nFAKER 用于伪造数据, ' '\nREGEX 用于正则校验' '' % str(key_tuple[0]))
class FileOperation(object): def __init__(self, file_name: str): """ 文件操作类,传入文件地址,将文件读入缓存中备用 """ self.L = Log("FileOperation").logger self.fileName = file_name def get_file_details(self) -> dict: """ 将yaml按标准进行读取到缓存中,可以参照http://www.bejson.com/validators/yaml_editor/ 格式化效果 将数据存放到data_file{}中 :return: """ self.L.critical("进入get_file_details") # 获取当前所处的文件夹上上一级文件夹的绝对路径 try: cur_path = os.path.abspath('../..') # 获取yaml文件路径 yaml_path = os.path.join(cur_path, self.fileName) # open方法打开直接读出来 yaml_open = open(yaml_path, 'r', encoding='utf-8') # 用load方法转为字典,存放在data_files内 data_file = yaml.load(yaml_open) self.L.debug('yaml文件解析为:%s' % data_file) return data_file except Exception as e: self.L.debug('yaml文件解析错误', e) def _format_checker(self): """ 检查文件请求数与断言数是否一致。 :return: """ data_files = {} try: data_file = self.get_file_details() for key in data_file: dict1 = data_file.get(key) data_keys = tuple(dict1.keys()) # 判断yaml文件字段是否缺失 if len(data_keys) == 3: self.L.info("yaml文件接口:%s字段数正确" % key) # 判断yaml文件字段名是否正确 if data_keys[0] == 'baseInfo': if data_keys[1] == 'interfaceParameters': if data_keys[2] == 'interfaceAsserts': self.L.info("yaml文件接口:%s字段名正确" % key) # 判断yaml文件内的interfaceParameters的case数和interfaceAsserts内的断言数是否对等 inf_para = dict1['interfaceParameters'] inf_asse = dict1['interfaceAsserts'] if len(inf_para) == len(inf_asse): self.L.info( "yaml文件接口:%s interfaceParameters的case数和interfaceAsserts" "内的断言数相同" % key) # 判断yaml文件的baseInfo的必填项是否缺失 base_info = dict1.get("baseInfo") if "requestMethod" in base_info: if "serverAddress" in base_info: self.L.info( "yaml文件接口:%s baseInfo内的必填项未缺失" % key) # 将检查通过的接口请求数据放入data_files{}中 data_files[key] = dict1 elif "serverAddress" not in base_info: self.L.debug( "yaml文件接口:%s baseInfo内的serverAddress字段缺失" % key) elif "requestMethod" not in base_info: self.L.debug( "yaml文件接口:%s baseInfo内的requestMethod字段缺失" % key) elif len(inf_para) != len(inf_asse): self.L.debug( 'yaml文件接口:%s interfaceParameters内case数与interfaceAsserts内断言' '数不相等' % key) elif data_keys[2] != 'interfaceAsserts': self.L.debug( 'yaml文件中接口:%s interfaceAsserts字段缺失' % key) elif data_keys[1] != 'interfaceParameters': self.L.debug( 'yaml文件中接口:%s interfaceParameters字段缺失' % key) elif data_keys[0] != 'baseInfo': self.L.debug('yaml文件中接口:%s baseInfo字段缺失' % key) elif len(data_keys) != 3: self.L.debug("yaml文件接口:%s 字段错误" % key) # 判断解析yaml文件获得的数据是否与检查通过的接口请求数据data_files是否相等,相等,则返回data_file{}, # 不相等则抛出异常,且不返回data_file{} if len(data_file) == len(data_files): self.L.info("解析的yaml文件数据放入data_file{}中") return data_file elif len(data_file) != len(data_files): self.L.debug("接口请求缺失") except Exception as e: self.L.debug('请求异常:%s' % e)
class KeyFaker(object): """ 按指定条件生成随机数据 """ def __init__(self): self.L = Log("KeyFaker").logger def get_some_things(self, faker_type: str = "" or list) -> str or int or float: """ 根据faker type生成响应数据,并返回结果 :param faker_type: mobile, farm, name, shop, int, float :return: """ data = "" fake = Factory().create('zh_CN') # 传入为list时,随机返回list中的一个值 if isinstance(faker_type, list): return random.choice(faker_type) elif isinstance(faker_type, str): # 若传入时下列关键字,返回对应的值 record = faker_type.lower() if record == 'mobile': data = fake.phone_number() self.L.info("随机生成手机号:%s" % data) elif record == 'farm': data = fake.company_prefix() + "的" + fake.name() + "在中国" + fake.city() + "的农场" self.L.info("随机生成农场名:%s" % data) elif record == 'name': data = fake.name() self.L.info("随机生成用户民:%s" % data) elif record == 'shop': data = fake.company() self.L.info("随机生成店铺名:%s" % data) elif record == 'integer': data = random.randint(1, 100) self.L.info("随机生成整数:%s" % data) elif record == 'decimal': data = random.uniform(1, 50) self.L.info("随机生成小数:%s" % data) elif record == 'text': data = (fake.text().replace("\n", " "))[:20] self.L.info("随机20位字符串:%s" % data) elif record == 'address': data = fake.address() self.L.info("随机生成地址:%s" % data) else: data = faker_type return data # if __name__ == '__main__': # f = KeyFaker() # f.get_some_things('farm')
def __init__(self): self.L = Log("KeyFaker").logger
def __init__(self): global global_dict self.L = Log("KeyGlobal").logger
def __init__(self): self.L = Log("KeySquery").logger
def __init__(self, file_name: str): """ 文件操作类,传入文件地址,将文件读入缓存中备用 """ self.L = Log("FileOperation").logger self.fileName = file_name
class Runner(object): """ 获取yaml中的请求和断言,并调用Parser进行解析 """ def __init__(self): self.L = Log("Runner").logger self.key = KeyGlobal() self.key.set_global_key('data_current', '{}') self.key.set_global_key('data_last', '{}') self.p = Dispatcher() def do_request(self, filename): """ 1.按data_file中的接口数量和接口测试次数,组装请求,并检查结果(断言引用Parser进行关键字解析) 2.需要记录当次测试,执行case的位置,第几个接口第几个步骤,每次执行完成后,将执行时的请求和响应封装到 data_current{}中 3.将上一次请求的响应和结果封装到 data_last{}中 4.执行请求前,需要检查请求中是否包含关键字 :return: """ self.L.critical("进入do_request") fop = FileOperation(filename) try: data_file = fop._format_checker() except Exception as e: raise Exception('当前文件异常') for interface_name in data_file.keys(): self.L.critical('当前接口名字为: %s' % interface_name) data = data_file.get(interface_name) self.L.debug('当前接口是否运行: %s' % data.get('baseInfo').get('directExecution')) if data.get('baseInfo').get('directExecution') is True or data.get( 'baseInfo').get('directExecution') is None: self.L.info("当前接口的baseInfo数据存入全局变量baseInfo,值为: %s" % data.get('baseInfo')) self.key.set_global_key("baseInfo", data.get('baseInfo')) method = data.get('baseInfo').get('requestMethod') self.L.debug('当前接口请求方法: %s' % method) # 同一个接口在yaml不同层次使用时,用!!!做区分 self.__choice_request(method, data, interface_name.split("!!!")[0]) def __check_resp(self, data, index): """ 对一套断言进行解析并断言 :param data: yaml文件中该接口的所有信息数据 :param index: 该请求的第几套断言 :return: """ if data.get('interfaceAsserts')[index] is not None: for k, v in data.get('interfaceAsserts')[index].items(): if isinstance(v, list): if k.find('RECORD') < 0: for j in v: # todo 查找该关键字不做断言需要优化 try: self.L.debug('断言是: %s, %s' % (self.p.key_dispatcher(k), self.p.key_dispatcher(j))) assert self.p.key_dispatcher( k) == self.p.key_dispatcher(j) except AssertionError: self.L.error('\n期望值: %s\n返回值: %s' % (self.p.key_dispatcher(j), self.p.key_dispatcher(k))) else: self.L.info("该关键字不做断言,数据为 %s %s:" % (k, v)) self.p.key_dispatcher(k, self.p.key_dispatcher(v)) else: if k.find('RECORD') < 0: # todo 查找该关键字不做断言需要优化 try: self.L.debug('断言是: %s, %s' % (self.p.key_dispatcher(k), self.p.key_dispatcher(v))) assert self.p.key_dispatcher( k) == self.p.key_dispatcher(v) except AssertionError: self.L.error('\n期望值: %s\n返回值: %s' % (self.p.key_dispatcher(v), self.p.key_dispatcher(k))) else: self.L.info("该关键字不做断言,数据为 %s %s:" % (k, v)) self.p.key_dispatcher(k, self.p.key_dispatcher(v)) else: self.L.warning('yaml断言部分为空') def __get_data_params(self, index): """ 对请求的一套参数进行解析并拼接成dict :param index: 该请求的第几套参数 :return: 返回dict """ data_params = {} # try: for k, v in index.items(): if k.find('RECORD') < 0: if isinstance(v, dict): v_dict = {} v_list = [] flag = 0 k = 1 for vk, vv in v.items(): vv = self.p.key_dispatcher(vv) if isinstance(vv, list): flag = 1 if k == 1: for _ in vv: v_dict = {} v_list.append(v_dict) k = k + 1 for i in range(len(vv)): v_list[i][self.p.key_dispatcher( vk)] = self.p.key_dispatcher(vv[i]) else: flag = 0 v_dict[self.p.key_dispatcher( vk)] = self.p.key_dispatcher(vv) if flag == 0: data_params[self.p.key_dispatcher(k)] = v_dict else: data_params[self.p.key_dispatcher( k)] = urllib.parse.quote(str(v_list), safe=string.printable) else: k = self.p.key_dispatcher(k) v = self.p.key_dispatcher(v) data_params[k] = v else: self.p.key_dispatcher(k, self.p.key_dispatcher(v)) # except Exception as e: # self.L.error('解析请求参数错误: %s' % e) self.L.debug(data_params) return data_params def __choice_request(self, method, data, interface_name): self.L.debug('当前接口的HOST: %s' % data.get('baseInfo').get('serverAddress')) url = data.get('baseInfo').get('serverAddress') + interface_name self.L.debug('当前接口的Headers: %s' % data.get('baseInfo').get('requestHeaders')) headers = data.get('baseInfo').get('requestHeaders') index = 0 for data_params in data.get('interfaceParameters'): self.L.info('当前接口请求解析前的参数: %s' % data_params) if data_params is not None: data_params = self.__get_data_params(data_params) self.L.info('当前接口请求解析后的参数: %s' % data_params) else: self.L.info('当前接口请求参数不需要解析') self.L.debug('写入全局变量data_last: %s' % self.key.get_global_value_by_key('data_current')) self.key.set_global_key( "data_last", "%s" % self.key.get_global_value_by_key('data_current')) try: self.L.info("--------------------发送请求中--------------------") self.L.info("请求URL: %s" % url) self.L.info("参数: %s" % data_params) if method == 'POSTFILE': file_path = str(list(data_params.values())[0]) file_key = list(data_params.keys())[0] file_name = file_path.split("/")[-1:][0] file_bin_data = open(file_path, 'rb') content_type = str( mimetypes.types_map.get( "." + file_path.split(".")[-1:][0], None)) files = { file_key: (file_name, file_bin_data, content_type) } resp = requests.post(url=url, files=files) elif method == 'GET': resp = requests.get(url=url, headers=headers, params=data_params) else: resp = requests.post(url, headers=headers, data=data_params) self.L.info("--------------------请求已完成--------------------") self.L.info("请求返回: %s" % resp.text) if resp.status_code == 200: try: data_dict = { 'req': data_params, 'resp': json.loads(resp.text) } except Exception as e: self.L.error(e) data_dict = {'req': data_params, 'resp': resp.text} self.L.debug('data_current: {"req": %s,"resp": %s}' % (data_params, resp.text)) self.key.set_global_key("data_current", data_dict) self.__check_resp(data, index) elif str(resp.status_code)[0] == '5': self.L.debug('服务器错误,状态为 %s' % str(resp.status_code)) elif str(resp.status_code)[0] == '4': self.L.debug('请求错误,状态为 %s' % str(resp.status_code)) elif str(resp.status_code)[0] == '3': self.L.debug('重定向请求,状态为 %s' % str(resp.status_code)) else: self.L.debug('不知名错误,状态为 %s' % str(resp.status_code)) except Exception as e: self.L.error('请求异常 %s' % e) index += 1
def __init__(self): """ 解析参数中的key或value信息,并返回各自关键字执行结果 """ self.L = Log("Dispatcher").logger
def __init__(self): self.L = Log("Runner").logger self.key = KeyGlobal() self.key.set_global_key('data_current', '{}') self.key.set_global_key('data_last', '{}') self.p = Dispatcher()
def __init__(self): self.L = Log("ProduceCaseYaml").logger
class SendRequest(object): def __init__(self): """ 请求类初始化本类日志 """ self.L = Log("SendRequest").logger @staticmethod def _json_format(data): if isinstance(data, str): json_str = json.loads(data) return json.dumps(json_str, ensure_ascii=False, sort_keys=True, indent=2, separators=(',', ': ')) elif isinstance(data, dict): return json.dumps(data, ensure_ascii=False, sort_keys=True, indent=2, separators=(',', ': ')) def post(self, url: str, body: object, headers: dict = None, cookies=None) -> str: """ 发送POST请求 :param url: 包括请求协议与请求路径的地址 :param body: 请求报文消息内容 :param headers: 请求报文头 :param cookies: 请求cookie :return: python3默认为bytes, 统一返回decode之后的str """ client = requests.session() response = client.post(url=url, data=body, headers=headers, cookies=cookies).content.decode('utf-8') # todo: 暂时未做各响应码的判断 self.L.debug("请求地址: %s" % url) self.L.debug("请求参数:\n%s" % self._json_format(body)) self.L.debug("响应内容:\n%s" % self._json_format(response)) return response def get(self, url: str, headers: dict = None) -> str: """ 发送GET请求 :param url: 包括请求协议与请求参数的地址 :param headers: 请求报文头 :return: python3默认为bytes, 统一返回decode之后的str """ client = requests.session() response = client.get(url=url, headers=headers).content.decode('utf-8') # todo: 暂时未做各响应码的判断 self.L.debug("请求地址: %s" % url) self.L.debug("响应内容:\n%s" % self._json_format(response)) return response def post_file(self, url: str, file_path: str, file_key: str, data_dict: dict = None) -> str: """ 发送POST文件请求 :param url: 包括请求协议与请求路径的地址 :param file_path: 文件的全路径 :param file_key: 传文件的键名 :param data_dict: 报文消息体内容 :return: python3默认为bytes, 统一返回decode之后的str """ file_name = file_path.split("/")[-1:][0] file_bin_data = open(file_path, 'rb') content_type = str( mimetypes.types_map.get("." + file_path.split(".")[-1:][0], None)) encode_file_name = urllib.parse.quote(file_name, safe='&?=:/', encoding='UTF-8', errors=None) files = {file_key: (encode_file_name, file_bin_data, content_type)} response = requests.post(url=url, data=data_dict, files=files).content.decode('utf-8') # todo: 暂时未做各响应码的判断, 后期优化到post方法中 self.L.debug("请求地址: %s" % url) self.L.debug("文件: %s" % file_path) self.L.debug("请求参数:\n%s" % self._json_format(data_dict)) self.L.debug("响应内容:\n%s" % self._json_format(response)) return response
def __init__(self): self.iterative_values = False self.L = Log("KeyGeneralMethods").logger
def __init__(self): self.L = Log("KeyResponse").logger self.li = [] self.kg = KeyGlobal()