def send_post(self, url, data, headers=None): """ 发送POST请求 :param url: url :param data: 请求数据 :param headers: 请求头 :return: 返回请求结果 """ # body = json.dumps(data, ensure_ascii=False).encode('UTF-8') body = data logger('body').debug(data) req = requests.Session() try: response_rst = req.post(url, json=body, headers=headers) rst = json.loads(response_rst.content) logger('rst: ').debug(rst) except Exception as e: logger().error('解析返回结果失败', e) if rst['code'] != 2000: logger().error('返回结果错误!状态码不为2000') logger().error(rst['message']) raise AssertionError(url, rst) else: return rst
def random_data_format(self, data): ''' 根据随机函数,生成数据 :param data: 格式化数据(类型为str) :return: ''' faker = Faker(locale='zh_CN') faker.add_provider(MyProvider) if '\\"' in data: data_md = data.replace('\\', '') self.interface_case_filed_list = re.findall( r',\"(.{1,30}?)":\"faker.', data_md) else: data_md = data logger('格式化数据').debug(data_md) for key, value in json.loads(data_md).items(): if 'faker.' in str(value): self.interface_case_filed_list.append(key) rely_filed_list = re.findall(r'\"(faker.*?)\"', data_md) for i, rely_filed in enumerate(rely_filed_list): # 提取key待优化 rely_filed = rely_filed.strip('\\') rely_value = str(eval(rely_filed)) self.interface_case_globals[ self.interface_case_filed_list[i]] = rely_value data_new = data.replace(rely_filed, rely_value) data = data_new return data
def __edit(self, sql, params): count = 0 try: self.connect() count = self.cursor.execute(sql, params) self.conn.commit() self.close() except Exception as e: logger().error(e) return count
def get_one(self, sql, params=()): result = None try: self.connect() self.cursor.execute(sql, params) result = self.cursor.fetchone() self.close() except Exception as e: logger().error(e) return result
def submit_data_and_verify(username, pwd, uri, body, expected_rst, sql_path, yml_path, send_method, **params): ''' 提交数据请求后,验证数据库结果 :param expected_rst: 预期结果 :param url: 请求url :param body: 请求报文 :param sql_path: sql文件 :param yml_path: yml格式化文件 :param send_method: 请求方法 :return: ''' sql_file = sql_path yml_file = yml_path # 随机函数格式化body、conditions、expected_rst # 格式化前置条件传的数据 setUp_dict = params['setUp'] body = format_data_by_setup(body, setUp_dict) conditions = format_data_by_setup(params['query'], setUp_dict) expected_rst = format_data_by_setup(expected_rst, setUp_dict) random_obj = RandomData() expected_rst = random_obj.get_random_data_value(expected_rst) # 登录 lg = Login(username, pwd) req = lg.req headers = { "Authorization": 'Bearer ' + lg.access_token, "k2": lg.defaultLoc, "k1": lg.defaultIns } # 发送接口请求 url = URL_INFO['TEST']['url'] + uri if send_method == 'POST': rst = req.send_post(url, data=body, headers=headers) else: rst = req.send_get(url, data=body, headers=headers) # 数据库查询校验 # 如果yml配置文件为空,则直接查询数据库,不需要进行格式化 if yml_path == '': db_rst = query_db(sql_file, query=conditions) logger('数据库查询结果:').debug(db_rst) if len(db_rst) > 0: fm_db_rst = json.dumps(db_rst[0]) else: logger('数据库查询结果:').error('数据库查询结果为空') fm_db_rst = '{}' else: fm_db_rst = get_db_format_data(sql_file, yml_file, query=conditions) logger('数据库查询结果根据YML格式化为:').debug(fm_db_rst) # 数据库查询结果和预期结果比对 logger('【=====expected_rst预期结果为:=====】').debug(expected_rst) logger('【=====数据库需比对数据:=====】').debug(fm_db_rst) compare_data(expected_rst, fm_db_rst)
def get_all(self, sql, params=()): list = [] try: self.connect() self.cursor.execute(sql, params) list = self.cursor.fetchall() self.close() except Exception as e: logger('数据库查询').error(e) if len(list) == 0: list = [] return list
def compare_data_sub(data_a, data_b): ''' 数据比对 :param data_a: 接口返回数据或自己设置的预期数据 :param data_b: 数据库查询结果 :return: ''' if isinstance(data_a, dict): if len(data_a.keys()) == len(data_b.keys()): for key in data_a.keys(): if isinstance(data_a[key], list) or isinstance( data_a[key], dict): compare_data_sub(data_a[key], data_b[key]) else: try: assert data_a[key] == data_b[key] except Exception as e: inf = "键值对%s %s != %s" % (key, data_a[key], data_b[key]) logger('结果比对失败:').error(inf) raise AssertionError(inf) else: inf = "data_1 键值对数量是%s != data2 键值对数量%s" % (len(data_a), len(data_b)) logger('结果比对失败:').error("字典长度不相等") raise AssertionError(inf) elif isinstance(data_a, list): if len(data_a) != len(data_b): inf = "data_1 长度是%s != data2 长度%s" % (len(data_a), len(data_b)) logger('结果比对失败:').error("list 长度不相等") raise AssertionError(inf) else: for data_a_value, data_b_value in zip(data_a, data_b): if isinstance(data_a_value, list) or isinstance( data_a_value, dict): compare_data_sub(data_a_value, data_b_value) else: try: assert data_a_value == data_b_value except Exception as e: inf = "列表%s中 %s != %s" % (data_a, data_a_value, data_b_value) logger('结果比对失败:').error(inf) raise AssertionError(inf) else: try: assert data_a == data_b except Exception as e: inf = " %s != %s" % (data_a, data_b) logger('结果比对失败:').error(inf) raise AssertionError(inf)
def format_data(original_data, target_yml): ''' 根据yml文件格式化json报文 :param original_data: 原始报文数据 :param target_yml: yml配置文件 :return: 格式化后结果 ''' with open(target_yml, encoding='utf-8') as file: target_yml_data = yaml.full_load(file) rst_json = {} rst_json = get_sub_data_interface(original_data, target_yml_data, rst_json) logger('【接口报文格式化结果为】').debug(rst_json) return json.dumps(rst_json, ensure_ascii=False)
def send_email(self): """ 发送邮件 """ # 第三方 SMTP 服务 mail_host = "smtp.163.com" # 设置服务器 mail_user = "******" # 用户名 mail_pass = "******" # 口令 # 获取口令地址 https://www.cnblogs.com/zhangshan33/p/11943755.html # 设置收件人和发件人 sender = '*****@*****.**' receivers = [ '*****@*****.**', '*****@*****.**', '*****@*****.**' ] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱 # 创建一个带附件的实例对象 message = MIMEMultipart() # 邮件主题、收件人、发件人 subject = '请查阅--测试报告' # 邮件主题 message['Subject'] = Header(subject, 'utf-8') # message['From'] = Header("{}".format(sender), 'utf-8') # 发件人 message['To'] = Header("{}".format(';'.join(receivers)), 'utf-8') # 收件人 message['From'] = '*****@*****.**' # 发件人 # message['To'] = '*****@*****.**' # 收件人 # 邮件正文内容 html 形式邮件 send_content = self.read_report() # 获取测试报告 html = MIMEText(_text=send_content, _subtype='html', _charset='utf-8') # 第一个参数为邮件内容 # 构造附件 att = MIMEText(_text=send_content, _subtype='base64', _charset='utf-8') att["Content-Type"] = 'application/octet-stream' file_name = 'report.html' att["Content-Disposition"] = 'attachment; filename="{}"'.format( file_name) # # filename 为邮件附件中显示什么名字 message.attach(html) message.attach(att) try: smtp_obj = smtplib.SMTP() smtp_obj.connect(mail_host, 25) # 25 为 SMTP 端口号 smtp_obj.login(mail_user, mail_pass) smtp_obj.sendmail(sender, receivers, message.as_string()) smtp_obj.quit() logger('发送邮件').debug("邮件发送成功!") except smtplib.SMTPException: logger('发送邮件').error("Error: 无法发送邮件!")
def connect_db_and_execute(sql, **params): ''' 连接数据库,并执行SQL语句 :param sql: SQL语句 :param params: 执行参数 :return: ''' sql = sql.format(**params['query']) logger('【执行SQL语句】').debug(sql) # 获取数据库db信息 db_name = (re.findall('(.+?)\.', sql.split('FROM')[1])[0]).replace('\t', '').strip() # 连接数据库并查询 db = MysqlHelper('mysql-test', db_name) query_rst = db.get_all(sql) return query_rst
def format_data_by_setup(data, setup): if isinstance(data, dict): data = json.dumps(data) if setup: data = data.replace('\\', '') filed_all = re.findall(r'\"@(.*?)"', data) for filed in filed_all: try: rely_value = setup[filed] new_data = data.replace('@'+filed, rely_value) except Exception as e: logger('接口间数据传递').error(filed) logger('接口间数据传递').error(e) data = new_data return json.loads(data)
def login(username, password): ''' 登录 :return: 返回token,请求对象 ''' url = '%s/app-sso/oauth/token?grant_type=password&username=%s&password=%s&verifyCode=&sessionId=' % (URL_INFO['TEST']['url'], username, password) headers = {"authorization": "Basic bm9idWc6Z2l2ZW1lZml2ZQ=="} req = HttpRquests() rst = req.send_post(url, data=None, headers=headers) if rst['code'] == 2000: logger('登录:').debug('登录成功') access_token = rst['body']['access_token'] else: logger('登录').error('登录失败') raise AssertionError('登录失败') return access_token, req
def format_body_by_rely(body, rely_data): ''' 根据依赖关系格式化请求参数 :param body: :param rely_data: :return: ''' rely_filed_list = re.findall(r'\"(\$.*?)\"', body) for rely_filed in rely_filed_list: try: rely_value = str(jsonpath.jsonpath(rely_data, rely_filed)[0]) new_body = body.replace(rely_filed, rely_value) except Exception as e: logger('接口间数据传递').error(rely_filed) logger('接口间数据传递').error(e) body = new_body return body
def get_default_setting(self, req, access_token): ''' 获取默认配置 :param req: session会话 :param access_token: token :return: ''' url = '%s/app-portal/get/default/setting' % (URL_INFO['TEST']['url']) headers = {"Authorization": 'Bearer ' + access_token} rst = req.send_post(url, data=None, headers=headers) if rst['code'] == 2000: defaultLoc = str(rst['body']['defaultLoc']) defaultIns = str(rst['body']['defaultIns']) else: logger('获取默认配置').error('获取默认配置失败') raise AssertionError('获取默认配置失败') return defaultLoc, defaultIns
def get_query_compare_rst(username, pwd, uri, body, expected_rst, sql_file_name, yml_file_name, send_method, **params): ''' 接口查询数据并和数据库结果比对 :param username: 登录用户名 :param pwd: 登录密码 :param uri: 请求uri :param body: 请求报文 :param expected_rst: 期望结果 :param sql_file_name: sql文件 :param yml_file_name: yml格式化文件 :param current_dir: 当前目录 :param send_method: 请求方法 :return: ''' sql_file = sql_file_name yml_file = yml_file_name # 格式化前置条件传的数据 setUp_dict = params['setUp'] body = format_data_by_setup(body, setUp_dict) conditions = format_data_by_setup(params['query'], setUp_dict) expected_rst = format_data_by_setup(expected_rst, setUp_dict) # 登录 lg = Login(username, pwd) access_token, req = lg.login() defaultLoc, defaultIns = lg.get_default_setting(req, access_token) headers = { "Authorization": 'Bearer ' + access_token, "k2": defaultLoc, "k1": defaultIns } # 发送接口请求 url = URL_INFO['TEST']['url'] + uri if send_method == 'POST': rst = req.send_post(url, data=body, headers=headers) else: rst = req.send_get(url, data=body, headers=headers) # 接口返回结果格式化 target_json = format_data(rst, yml_file) logger('接口返回结果格式化结果:').debug(target_json) # 如果期望结果expected_rst不为空,则不需要查询数据库,直接使用expected_rst与target_json比对 if expected_rst: compare_data(target_json, expected_rst) else: # 执行数据库查询并返回格式化后的结果 fm_db_rst = get_db_format_data(sql_file, yml_file, query=conditions) logger('数据库查询结果后根据YML格式化:').debug(fm_db_rst) # 数据库查询结果和接口返回结果比对 logger('【=====接口需比对数据:=====】').debug(target_json) logger('【=====数据库需比对数据:=====】').debug(fm_db_rst) compare_data(target_json, fm_db_rst)
def compare_data(json1, json2): ''' 数据对比 :param json1: 数据1 :param json2: 数据2 :return: ''' # 将str转dict if isinstance(json1, str): json1 = json.loads(json1) if isinstance(json2, str): json2 = json.loads(json2) if type(json1) == type(json2): if json1 == {} or json2 == {}: logger('【结果比对失败】').error('json1 或者 json2数据为空') raise AssertionError('json1 或者 json2数据为空') else: compare_data_sub(json1, json2) else: raise AssertionError('json1和json2数据类型不一致')
def case(request): ''' 测试用例前置和后置 :param request: :return: ''' caseData = request.param if isinstance(caseData, dict): setUp = caseData['setUp'] # 如果有前置条件,则将前置条件的返回结果存取 if setUp: logger('前置条件').debug('【测试前置条件测试】') setUp_value = globals()[setUp]() request.param['setUp'] = setUp_value # 如果是场景类型 elif isinstance(caseData, list): setUp = caseData[0]['setUp'] # 如果有前置和后置条件,则将前置条件的返回结果存取 if setUp: logger('前置条件').debug('【测试前置条件执行】') setUp_value = globals()[setUp]() request.param[0]['setUp'] = setUp_value return request.param
def send_get(self, url, data, headers=None): """ 发送GET请求 :param url: url :param data: 请求数据 :param headers: 请求头 :return: 返回请求结果 """ logger('body').debug(data) req = requests.Session() try: response_rst = req.get(url, params=data, headers=headers) rst = json.loads(response_rst) except Exception as e: logger().error('解析返回结果失败', e) if rst['code'] != 2000: logger().error('返回结果错误!状态码不为2000') raise AssertionError(url, rst) else: return rst
def scene_middle_verify(username, pwd, scene_data, setUp=None): ''' 场景类自动化用例验证 :param username: 登录用户名 :param pwd: 登陆密码 :param scene_data: 场景类的驱动数据 :param setUp: 前置条件返回的结果数据 :return: ''' lg = Login(username, pwd) req = lg.req headers = { "Authorization": 'Bearer ' + lg.access_token, "k2": lg.defaultLoc, "k1": lg.defaultIns } # 将接口依赖关系存存入dict interface_relations = {} for interface_data in scene_data: interface_relations[interface_data['uri']] = interface_data['rely'] # 解析接口依赖关系,梳理接口依赖关系;决定接口执行顺序,(场景接口数据根据接口依赖重新排序) scene_data_new = [] for interface, rely_interface in interface_relations.items(): for scene in scene_data: if rely_interface == "" and rely_interface == scene['rely']: scene_data_new.append(scene) for scene_new in scene_data_new: for scene in scene_data: if scene['rely'] == scene_new['uri']: scene_data_new.append(scene) # 然后针对每个接口发送请求,根据接口依赖取值,并保存;提供给其他依赖与此接口的返回值; # 接口返回结果 reponse_rst_dict = {} for index, interface_data in enumerate(scene_data_new): # 发送接口请求 uri = interface_data['uri'] # 根据场景的前置条件格式化body body = format_data_by_setup(interface_data['body'], setUp) if interface_data['rely']: # 获取依赖的数据,并格式化body,将依赖的数据写入填充到body里面 body = format_body_by_rely( interface_data['body'], reponse_rst_dict[interface_data['rely']]) # 打印body日志 logger('【接口' + str(index + 1) + '】请求报文body为:').debug(body) if isinstance(body, str): body = json.loads(body) url = URL_INFO['TEST']['url'] + uri if interface_data['send_method'] == 'POST': rst = req.send_post(url, data=body, headers=headers) else: rst = req.send_get(url, data=body, headers=headers) # 打印接口返回结果日志 logger('【接口' + str(index + 1) + '】返回结果为:').debug(rst) # 如果SQL执行字段为空,则保存接口返回结果; 否则查询数据库结果保存 if interface_data['sql'] and index < len(scene_data_new) - 1: sql_file = interface_data['sql'] condition = json.loads(interface_data['condition']) db_rst = query_db(sql_file, query=condition) reponse_rst_dict[uri] = db_rst[0] else: if rst: reponse_rst_dict[uri] = rst else: logger(uri).error('接口返回结果错误!') raise AssertionError(uri, '接口返回结果错误') # 最后一个接口结束后,验证场景结果 # 如果最后一个接口是查询类,则写SQL查询校验 if scene_data_new[-1]['interface_type'] == 0: rst = reponse_rst_dict[scene_data_new[-1]['uri']] yml_file = scene_data_new[-1]['yml'] sql_file = scene_data_new[-1]['sql'] expected_rst = scene_data_new[-1]['expected_rst'] # condition = json.loads(scene_data_new[-1]['condition']) condition = json.loads( format_body_by_rely(scene_data_new[-1]['condition'], reponse_rst_dict[interface_data['rely']])) target_json = format_data(rst, yml_file) logger('接口返回结果根据YML格式化为:').debug(target_json) # 如果expected_rst 期望结果不为空,则直接expected_rst与target_json比对; if expected_rst: compare_data(target_json, expected_rst) else: # 查询数据库,执行数据库查询并返回格式化后的结果 fm_db_rst = get_db_format_data(sql_file, yml_file, query=condition) logger('数据库查询结果根据YML格式化为:').debug(fm_db_rst) # 数据库查询结果和接口返回结果比对 logger('【=====接口需比对数据:=====】').debug(target_json) logger('【=====数据库需比对数据:=====】').debug(fm_db_rst) compare_data(target_json, fm_db_rst) # 如果最后一个接口是提交类,则直接执行SQL查询结果 else: # 数据库查询校验 expected_rst = scene_data_new[-1]['expected_rst'] yml_file = scene_data_new[-1]['yml'] sql_file = scene_data_new[-1]['sql'] # condition = json.loads(scene_data_new[-1]['condition']) condition = json.loads( format_body_by_rely(scene_data_new[-1]['condition'], reponse_rst_dict[interface_data['rely']])) if yml_file == '': db_rst = query_db(sql_file, query=condition) logger('数据库查询结果:').debug(db_rst) if len(db_rst) > 0: fm_db_rst = json.dumps(db_rst[0]) else: logger('数据库查询结果:').error('数据库查询结果为空') fm_db_rst = '{}' else: fm_db_rst = get_db_format_data(sql_file, yml_file, query=condition) logger('数据库查询结果根据YML格式化为:').debug(fm_db_rst) # 数据库查询结果和预期结果比对 logger('【=====expected_rst预期结果为:=====】').debug(expected_rst) logger('【=====数据库需比对数据:=====】').debug(fm_db_rst) compare_data(expected_rst, fm_db_rst)