def login(self): """ 触发登录接口请求并XX业务请求令牌Token值 :return: """ logger.info('[Start]:开始请求登录XX业务{}环境后台服务...'.format(self.env)) login_res = RequestTool().post(url=self.login_url, headers={"Content-Type": "application/json;charset=UTF-8"}, data=json.dumps( {"username": "******".format(self.username), "password": "******".format(encrypt.MD5(self.password)), "verificationCode": "{}".format(self.validcode), "code": "hlwyy", "userType": 3, "token": ""}), verify=True) response_str = login_res.text.split("\"")[1] # response_str = login_res.text decrypt_login_res = ResponseDecrypt(response_str=response_str).decrypt() decrypt_login_res = re.sub(r"\n|\t|\r|\r\n|\n\r|\x08|\\", "", decrypt_login_res) login_res_dict = json.loads(decrypt_login_res) if login_res_dict.get("err") == 0 and login_res_dict.get("errmsg") == '操作成功': self.token = login_res_dict['data']['token'] logger.info('[Success]:XX业务{}环境后台服务登录成功, 请求令牌Token值为: {}'.format(self.env, self.token)) return self else: logger.warning( "[WARNING]:XX业务{}环境后台服务登录失败,接口响应为【{}】,重试登录中...".format(self.env, login_res_dict)) sys.exit(1)
def check_duplicate_urlpath(self): """ 检测映射接口文档中匹配的UrlPath是否存在重复值 :return: """ pass_flag = False try: if self.check_exist_urlpath is True: case_doc_urlpath_veriry = { i: j for i, j in dict( Counter([ str(i).strip() for i in self.case_doc_df["UrlPath"].values.tolist() ])).items() if j > 1 } if case_doc_urlpath_veriry == {}: logger.info( "[Success]:映射接口文档中匹配的UrlPath是否存在重复值检测通过,其匹配的UrlPath均唯一." ) return True if case_doc_urlpath_veriry != {}: logger.warning( '[WARNING]:检测到映射接口文档中存在重复的UrlPath,检测结果:{},请仔细核对并明确需求!'. format(case_doc_urlpath_veriry)) pass_flag = False return pass_flag else: logger.error( "[Fail]:Swagger接口文档与接口抓包文档进行UrlPath数据匹配检测失败,请仔细检查相关文档数据!") sys.exit(1) except Exception: logger.exception( "[Exception]:检测映射接口文档中匹配的UrlPath是否存在重复值过程中发生异常,请检查!") return False
def check_match_urlpath(self): """ 检测接口抓包文档与映射接口文档中匹配的UrlPath是否存在一一映射匹配关系 :return: """ try: case_doc_df_count = self.case_doc_df.shape[0] capture_doc_df_count = self.capture_doc_cid_check.__len__() if case_doc_df_count < capture_doc_df_count: swagger_doc_less_set = set([ str(i).strip() for i in self.capture_doc_df["CID"].values.tolist() ]) - set([ str(i).strip() for i in self.case_doc_df["UrlPath"].values.tolist() ]) logger.warning( "[WARNING]:检测到映射接口文档中缺少接口抓包文档中映射匹配的UrlPath,检测结果:{},请检查相关接口文档是否符合需求或手动追加!" .format(swagger_doc_less_set)) return False if case_doc_df_count == capture_doc_df_count: logger.info( "[Success]:映射接口文档与接口抓包文档中UrlPath一一映射匹配关系检测通过,二者存在一一映射匹配的UrlPath." ) return True except Exception: logger.exception( "[Exception]:检测映射接口文档与接口抓包文档进行UrlPath一一映射匹配关系过程中发生异常,请检查!") return False
def __init__(self, swagger_api_file, capture_api_file): """ 初始化指定Swagger接口文档和接口抓包文档 :param swagger_api_file: Swagger接口文档(Excel文件) :param capture_api_file: 接口抓包文档(如Charles、Fiddler或微信开发者工具完成抓包并同步文档) """ if os.path.exists(swagger_api_file): if os.path.exists(capture_api_file): self.swagger_doc_df = DataframeOperation( test_case_file=swagger_api_file).df.rename( columns={"ID": "AID"}) self.capture_doc_df = DataframeOperation( test_case_file=capture_api_file).df.rename(columns={ "ID": "CID" }).sort_values(by=["Group", "CID"], ascending=True) self.capture_doc_cid_check = [ str(i).strip() for i in list( dict( self.capture_doc_df.groupby( ["CID"], sort=True).groups).keys()) if i != '' ] self.case_doc_df = self.swagger_doc_df.loc[ self.swagger_doc_df["UrlPath"].isin( self.capture_doc_cid_check)].sort_values( by="UrlPath", ascending=True) else: logger.warning( '[WARNING]:接口抓包文档"{}"当前并不存在,请先完成相关接口抓包操作!'.format( capture_api_file)) sys.exit(1) else: logger.warning( '[WARNING]:Swagger接口文档"{}"当前并不存在,请先完成接口文档生成操作!'.format( swagger_api_file)) sys.exit(1)
def check_exist_urlpath(self): """ 检测接口抓包文档在Swagger接口文档中是否存在匹配的UrlPath :return: """ try: if self.capture_doc_df["CID"].shape[ 0] == 0 or self.capture_doc_cid_check == []: logger.warning( "[WARNING]:接口抓包文档中UrlPath当前无任何填充数据,请首先完成接口抓包工作!") sys.exit(1) if self.case_doc_df["UrlPath"].shape[0] == 0: logger.warning( "[WARNING]:接口抓包文档中UrlPath在Swagger接口文档中未匹配到任何数据,请检查接口抓包文档和Swagger接口文档是否符合需求!" ) return False if self.case_doc_df["UrlPath"].shape[ 0] != 0 and self.capture_doc_df["CID"].shape[0] != 0: logger.info( "[Success]:Swagger接口文档及接口抓包文档中UrlPath是否存在匹配数据检测通过,二者UrlPath存在匹配数据." ) return True except Exception: logger.exception( "[Exception]:检测接口抓包文档中UrlPath在Swagger接口文档中是否存在匹配数据过程中发生异常,请检查!" ) return False
def check_match_urlpath(self): """ 检测Swagger接口文档与接口抓包文档中的UrlPath是否映射匹配 :return: """ try: case_doc_df_count = self.case_doc_df.shape[0] capture_doc_df_count = self.capture_doc_df.shape[0] if case_doc_df_count == 0: logger.warning( "[WARNING]:检测到Swagger接口文档中无任何映射匹配的UrlPath,请检查相关接口文档是否符合需求或手动追加!" ) return False if case_doc_df_count < capture_doc_df_count: swagger_doc_less_set = set([ str(i).strip() for i in self.capture_doc_df["CID"].values.tolist() ]) - set([ str(i).strip() for i in self.case_doc_df["UrlPath"].values.tolist() ]) logger.warning( "[WARNING]:检测到Swagger接口文档中缺少映射匹配的UrlPath,检测结果:{},请检查相关接口文档是否符合需求或手动追加!" .format(swagger_doc_less_set)) return False if case_doc_df_count == capture_doc_df_count: logger.info( "[Success]:Swagger接口文档与接口抓包文档中UrlPath映射匹配规则检测通过,二者存在数量相等且映射匹配的UrlPath." ) return True except Exception: logger.exception( "[Exception]:检测Swagger接口文档与接口抓包文档进行UrlPath映射匹配规则过程中发生异常,请检查!") return False
def __init__(self, dbtype, env, dbname=None): """ 指定数据库类型、所处环境及数据库名称 :param dbtype: 数据库类型(当前支持mysql/redis/mongodb) :param env: 所处环境 :param dbname: 数据库名称 """ try: if str(dbtype).lower() not in [r'mysql', r'redis', r'mongodb']: raise DatabaseTypeError if str(env).lower() not in [r'test', r'stg', r'dev', r'qa']: raise EnvironmentTypeError else: self.dbtype = str(dbtype).lower() self.env = str(env).lower() self.dbname = dbname except DatabaseTypeError: logger.warning( '[WARNING]:检测到非法或不支持的数据库类型"{}",当前仅支持[mysql, redis, mongodb]'. format(dbtype)) sys.exit(1) except EnvironmentTypeError: logger.warning( '[WARNING]:检测到非法或不支持的测试环境"{}",当前仅支持[test, stg, dev, qa]'. format(env)) sys.exit(1)
def send(self, msgtype): """ 发送钉钉机器人消息提醒 :param msgtype: 消息提醒类型 :return: """ try: if msgtype not in [ 'text', 'link', 'markdown', 'actionCard', 'feedCard' ]: logger.warning( '[WARNING]:检测到非法消息类型"{}",当前仅支持text、link、markdown、actionCard、feedCard,请重新指定!' .format(msgtype)) sys.exit(1) url = "https://oapi.dingtalk.com/robot/send?access_token={}×tamp={}&sign={}".format( self.token, self.timestamp, self.get_sign) headers = {'Content-Type': 'application/json'} response = requests.request("POST", url, headers=headers, data=self.generate_msg(msgtype)) result = response.json() if result.get("errcode") == 0 and result.get("errmsg") == 'ok': logger.info("[Done]:钉钉机器人消息提醒成功.") else: logger.warning( "[WARNING]:钉钉机器人消息提醒失败,接口响应为【{}】,开始重试...".format(result)) sys.exit(1) except Exception: logger.exception("[Exception]:发送钉钉机器人消息提醒过程中发生异常,请检查!") sys.exit(1)
def __init__(self, env, dbname, auth_dict=None): """ 指定所处环境及数据库名称 :param env: 所处环境 :param dbname: 数据库名称 :param auth_dict: 授权字典,样例格式为{"auth_db":"admin", "auth_username":"******", "auth_password":"******"} """ mongodbParam = YamlConfig(config=APPLICATION_CONFIG_FILE).get( "mongodb", 1).get(env) self.mongodb = MongoClient(host=mongodbParam['host'], port=mongodbParam['port']) if not mongodbParam.__contains__("auth") and auth_dict is None: logger.warning("请在应用配置文件中指定MongoDB授权信息,或者通过auth_dict字典进行指定!") sys.exit(1) if auth_dict is not None: if isinstance(auth_dict, dict): auth_db = auth_dict.get("auth_db") auth_username = auth_dict.get("auth_username") auth_password = str(auth_dict.get("auth_password")) auth = eval("self.mongodb.{}".format(auth_db)) auth.authenticate(auth_username, auth_password) self.db_client = eval("self.mongodb.{}".format(dbname)) else: logger.warning("auth_dict授权信息必须为字典形式,请检查!") sys.exit(1) if auth_dict is None and mongodbParam.__contains__("auth"): mongodbAuth = mongodbParam.get("auth") auth_db = mongodbAuth.get("db") auth_username = mongodbAuth.get("username") auth_password = str(mongodbAuth.get("password")) auth = eval("self.mongodb.{}".format(auth_db)) auth.authenticate(auth_username, auth_password) self.db_client = eval("self.mongodb.{}".format(dbname))
def generate(self, test_case_file, sep=r'|', encoding='utf-8'): """ 生成测试用例文件(当前支持Excel文件[xls/xlsx]及文本文件[csv/txt]) :param test_case_file: 测试用例文件路径 :param sep: 文件分隔符,默认"|"。 :param encoding: 文件编码格式。 :return: """ logger.info("[Initial]:开始自动评估测试用例生成条件......") file_extend = str(os.path.splitext(test_case_file)[-1]).lower() try: test_case_df = self.load_test_case if file_extend not in [".csv", ".txt", ".xls", ".xlsx"]: logger.warning( "[WARNING]:自动生成的测试用例文件扩展名当前仅支持[csv、txt、xls、xlsx],请检查!") sys.exit(1) if file_extend in ['.xls', '.xlsx']: logger.info( "[Loading]:开始自动生成{}格式测试用例文件......".format(file_extend)) test_case_df.to_excel(test_case_file, index=False) if file_extend in ['.csv', '.txt']: logger.info( "[Loading]:开始自动生成{}格式测试用例文件......".format(file_extend)) test_case_df.to_csv(test_case_file, sep=sep, index=False, header=True, encoding=encoding) logger.info('[Done]:{}格式测试用例文件已经成功自动生成,路径为"{}".'.format( file_extend, test_case_file)) except Exception: logger.exception( "[Exception]: {}格式测试用例文件自动生成过程中发生异常,请检查!".format(file_extend)) sys.exit(1)
def load_test_case(self): """ 依据swagger接口文档及接口抓包文档生成测试用例 :return: """ try: if self.check_duplicate_urlpath is True: if self.check_match_urlpath is True: self.case_doc_df["Platform"] = [ str(platform).strip().capitalize() for platform in self.capture_doc_df["Platform"].values.tolist() ] self.case_doc_df["Level"] = [ str(level).strip().upper() for level in self.capture_doc_df["Level"].values.tolist() ] self.case_doc_df["Active"] = True self.case_doc_df["Group"] = self.capture_doc_df[ "Group"].values.tolist() self.case_doc_df["Order"] = self.capture_doc_df[ "Order"].values.tolist() self.case_doc_df["RequestPath"] = self.capture_doc_df[ "Path"].values.tolist() self.case_doc_df["RequestHeader"] = self.capture_doc_df[ "Header"].values.tolist() self.case_doc_df["RequestParam"] = self.capture_doc_df[ "Param"].values.tolist() self.case_doc_df["RequestData"] = self.capture_doc_df[ "Data"].values.tolist() self.case_doc_df["RequestFile"] = self.capture_doc_df[ "File"].values.tolist() self.case_doc_df["DependencyInfo"] = self.capture_doc_df[ "Dependency"].values.tolist() self.case_doc_df["AssertInfo"] = self.capture_doc_df[ "Assert"].values.tolist() self.case_doc_df = self.case_doc_df.sort_values( by="Order", ascending=True) self.case_doc_df = self.case_doc_df.reset_index( drop=True).reset_index().rename( columns={"index": "ID"}) self.case_doc_df["ID"] = self.case_doc_df["ID"].apply( lambda x: "TC_{}".format(x + 1)) return self.case_doc_df else: logger.warning( "[WARNING]:Swagger接口文档与接口抓包文档中UrlPath映射匹配规则检测未通过,请查看相关检测结果!" ) sys.exit(1) else: logger.warning( "[WARNING]:Swagger接口文档或接口抓包文档中各自UrlPath列是否存在重复值检测未通过,请查看相关检测结果!" ) sys.exit(1) except Exception: logger.exception( "[Exception]:依据Swagger接口文档及接口抓包文档自动生成测试用例过程中发生异常,请检查!") sys.exit(1)
def get_token(self): """ 获取请求令牌Token值 :return: """ if self.token is None: logger.warning("[WARNING]:检查到XX业务{}环境后台服务请求令牌Token值为空, 请先登录!".format(self.env)) sys.exit(1) return self.token
def get_job_info(self, job_name): """ 获取构建项目信息 :param job_name: 构建项目名称 :return: """ try: if self.session.job_exists(name=job_name): info = self.session.get_job_info(name=job_name) return info else: logger.warning( '[WARNING]: Jenkins构建项目"{}"并不存在,请检查!'.format(job_name)) sys.exit(1) except Exception: logger.exception('查看Jenkins构建项目"{}"过程中发生异常,请检查!'.format(job_name))
def write_column(self, col, value, style="black"): """ 整列赋值并设置字体颜色。 :param col: 列索引 :param value: 赋值 :param style: 字体颜色样式(默认黑色) :return: """ if str(style).lower() not in [ "orange", "green", "red", "boldorange", "gray", "black" ]: logger.warning( '当前字体样式不支持"{}"颜色,请选择如下字体颜色["orange", "green", "red", "boldorange","gray", "black"]' .format(style)) for row in range(1, self.row_count): self.write(row, col, value, style) return self
def generate_report(cls): """ 根据json结果文件自动生成Allure测试报告。 """ try: if cls.check_result() is True: command = "allure generate {0} -o {1} --clean".format(ALLURE_RESULT, ALLURE_REPORT) time.sleep(1) logger.info('开始执行Allure测试报告生成命令:"{}"'.format(command)) run_command(command) logger.info("[Done]:已经成功生成Allure测试报告.") else: logger.warning("[Warning]:由于未检测到Allure测试结果json文件,停止生成Allure测试报告!") except CalledProcessError: logger.exception("[Exception]:Allure测试报告生成命令执行失败!") except Exception: logger.exception("[Exception]:生成Allure测试报告过程中发生异常,请检查!")
def write(self, row, col, value, style="black"): """ 写入单元格并设置字体颜色。 :param row: 行索引 :param col: 列索引 :param value: 赋值 :param style: 字体颜色样式(默认黑色) :return: """ if str(style).lower() not in [ "orange", "green", "red", "boldorange", "gray", "black" ]: logger.warning( '当前字体样式不支持"{}"颜色,请选择如下字体颜色["orange", "green", "red", "boldorange", "gray", "black"]' .format(style)) self.w.write(row, col, value, self.__STYLE_DICT.get(style)) return self
def generate_msg(self, msgtype): """ 构造钉钉机器人消息体 :param msgtype: 消息提醒类型(当前仅支持Text/Link/MarkDown/ActionCard/FeedCard) :return: """ try: if msgtype == 'text': return json.dumps(self.ding.get('text')) if msgtype == 'link': return json.dumps(self.ding.get('link')) if msgtype == 'markdown': return json.dumps(self.ding.get('markdown')) if msgtype == 'actionCard': return json.dumps(self.ding.get('actionCard')) if msgtype == 'feedCard': return json.dumps(self.ding.get('feedCard')) except Exception: logger.warning("[WARNING]:构造钉钉机器人消息体过程中发生异常,请检查!")
def write_column(self, col, value, style="black"): """ 整列赋值并设置字体颜色。 :param col: 列索引 :param value: 赋值 :param style: 字体颜色样式(默认黑色) :return: """ if str(style).lower() not in [ "orange", "green", "red", "boldorange", "gray", "black" ]: logger.warning( '当前字体样式不支持"{}"颜色,请选择如下字体颜色["orange", "green", "red", "boldorange","gray", "black"]' .format(style)) for row in range(2, self.row_count + 1): self.sheet_reader.cell(row, col + 1).value = value self.sheet_reader.cell(row, col + 1).font = self.__STYLE_DICT[style] return self
def __init__(self, swagger_doc_urls, swagger_domain_urls=None): """ 初始化Swagger接口文档。 :param swagger_doc_urls: Swagger接口地址, 必须使用";"分隔。 :param swagger_domain_urls: Swagger接口域名, 必须使用";"分隔。 """ # Swagger接口文档地址列表 swagger_doc_url_list = [ swagger_doc_url.strip() for swagger_doc_url in str(swagger_doc_urls).split(";") if swagger_doc_url != "" ] self.swagger_protocols = [ swagger_doc_url.split(":")[0] for swagger_doc_url in swagger_doc_url_list ] self.swagger_domain_url_list = [ swagger_domain.strip() for swagger_domain in str(swagger_domain_urls).split(";") if swagger_domain != "" ] if swagger_domain_urls is not None else swagger_domain_urls if isinstance(self.swagger_domain_url_list, list): if self.swagger_domain_url_list.__len__( ) < swagger_doc_url_list.__len__(): logger.warning( "[Warning]:当前配置文件中已有的Swagger替换域名少于Swagger接口文档地址列表,请检查并明确域名替换规则![当前会默认启用首个域名替换全部Swagger请求域]" ) self.swagger_domain_url_list = [ self.swagger_domain_url_list[0] for count in range(swagger_doc_url_list.__len__()) ] # JSON样式Swagger接口文档列表 self.swagger_res_dict_list = [] try: session = requests.session() for swagger_doc_url in swagger_doc_url_list: swagger_res = session.get(url=swagger_doc_url) self.swagger_res_dict_list.append(json.loads(swagger_res.text)) except Exception: logger.exception("[Exception]: 请求Swagger过程中发生异常,请检查网络或服务状态!") sys.exit(1)
def sync_history(cls): """ 追加Allure历史追溯信息 :return: """ ALLURE_REPORT_HISTORY = os.path.join(ALLURE_REPORT, "history") ALLURE_RESULT_HISTORY = os.path.join(ALLURE_RESULT, "history") try: if os.path.exists(ALLURE_RESULT_HISTORY): raise FileExistsError if os.path.exists(ALLURE_REPORT_HISTORY): time.sleep(1) shutil.copytree(ALLURE_REPORT_HISTORY, ALLURE_RESULT_HISTORY) logger.info("[Success]:已经成功同步Allure历史追溯信息.") else: logger.warning('[WARNING]:Allure历史追溯信息"{}"当前并不存在,无法完成同步!'.format(ALLURE_REPORT_HISTORY)) except FileExistsError: logger.exception('[Exception]:已同步Allure历史追溯信息至"{}",无需再次同步!'.format(ALLURE_RESULT_HISTORY)) except Exception: logger.exception("[Exception]:同步Allure历史追溯信息过程中发生异常,请检查!")
def get_swagger_to_excel(self, file_path): """ 根据Swagger生成Excel接口文档。 :param file_path: Excel文件路径。 :return: """ try: if str(os.path.splitext(file_path)[-1]).lower() not in [ ".xls", ".xlsx" ]: logger.warning("[WARNING]:生成的Excel文件扩展名必须为xls或xlsx格式,请检查!") sys.exit(1) logger.info("[Initial]:开始通过Swagger转换生成Excel接口文档[依赖环境配置]......") swagger_dataframe = self.get_swagger_to_dataframe swagger_dataframe.to_excel(file_path, index=False) logger.info( '[Done]:已通过Swagger成功转换生成Excel接口文档,路径为"{}".'.format(file_path)) except Exception: logger.exception("[Exception]: 通过Swagger生成Excel接口文档失败,请检查原因!") sys.exit(1)
def check_result(cls, check_count=CHECK_COUNT): """ 检查Allure测试结果json文件是否生成。 :param check_count: 检测次数。 :return: """ check_result = None for count in range(check_count): if os.listdir(ALLURE_RESULT) == list() and count < check_count: if count == check_count - 1: logger.warning("仍未检测到Allure测试结果json文件,已达检测次数上限({}),停止检测。".format(check_count)) check_result = False break logger.info("未检测到Allure测试结果json文件,可能正在生成......") time.sleep(2) continue if os.listdir(ALLURE_RESULT) != list(): logger.info("[Success]:已检测到Allure测试结果json文件.") check_result = True break return check_result
def check_empty_urlpath(self): """ 检查Swagger接口文档及接口抓包文档中UrlPath是否全部填充 :return: """ try: if self.swagger_doc_df["UrlPath"].shape[ 0] == 0 or self.capture_doc_df["CID"].shape[0] == 0: logger.warning( "[WARNING]:Swagger接口文档或接口抓包文档中UrlPath列当前无任何填充数据,请检查并完全填充相关数据!" ) return False if self.swagger_doc_df["UrlPath"].shape[ 0] != 0 and self.capture_doc_df["CID"].shape[0] != 0: logger.info( "[Success]:Swagger接口文档及接口抓包文档中UrlPath列完全填充状态检测通过,二者UrlPath列均已填充完全." ) return True except Exception: logger.exception( "[Exception]:检测Swagger接口文档及接口抓包文档中UrlPath列是否完全填充过程中发生异常,请检查!") return False
def __init__(self, ding_notify_file, preview_mode=False): """ 从应用配置文件获取钉钉请求token及签名secret :param ding_notify_file: 钉钉消息模板文件 :param preview_mode: 测试报告截图预览 """ try: DingTools.__init__(self) if not os.path.exists(ding_notify_file): raise FileNotFoundError with open(file=ding_notify_file, mode=r'r', encoding='utf-8') as ding_file: if preview_mode is False: self.ding = json.loads(ding_file.read().replace( "ip_address", get_ip()).replace("![Allure](report_url)", "> 🐾🐾🐾 ~ {}".format(get_ip()))) else: self.ding = json.loads(ding_file.read().replace( "ip_address", get_ip()).replace( "report_url", face_bed(pic=capture_image( width=1440, height=797, url="http://localhost:5000/allure", sleep=10, pic=os.path.join(RESOURCE_PATH, "Allure", "Allure.png")), alias="Allure.png"))) self.token = YamlConfig( config=APPLICATION_CONFIG_FILE).get("ding")["token"] self.secret = YamlConfig( config=APPLICATION_CONFIG_FILE).get("ding")["secret"] self.timestamp = str(round(time.time() * 1000)) except FileNotFoundError: logger.warning( '[WARNING]:钉钉消息通知模板文件"{}"当前并不存在,请检查!'.format(ding_notify_file)) sys.exit(1)
def send(self, message='FYI', image_flag=False, *filepath): ''' 触发邮件发送 :param message: 邮件文本信息 :param image_flag: 是否发送图片 :param filepath: 邮件附件元组 :return: ''' self.message = message self.image_flag = image_flag for p in filepath: if os.path.isdir(p): self.files = list() for f in os.listdir(p): self.files.append(os.path.join(p, f)) self.checkAtType(self.files) elif os.path.isfile(p): self.files = p self.checkAtType(self.files) else: self.files = p filename = re.split(r'[\\|/]', str(p))[-1] logger.warning('注意! 邮件附件"{0}"的路径"{1}"可能无效, 附件无法上传.'. format(filename, p)) try: logger.info("开始发送测试结果邮件......") session = self.createSession() session.sendmail(self.sender, self.receiver.split(';'), self.build_all_msg().as_string() if self.image_flag else self.build_html_msg().as_string() ) except (gaierror and error): logger.exception('邮件发送失败! ~ 无法连接到SMTP服务器, 请检查网络以及邮件配置.') else: logger.info('[Done]:{0}邮件发送成功! 收件人:{1}.'.format(self.title, self.receiver)) session.quit() session.close()
def get_swagger_to_csv(self, file_path, sep=r',', encoding='utf-8'): """ 根据Swagger生成CSV接口文档。 :param file_path: CSV文件路径。 :param sep: 文件分隔符,默认","。 :param encoding: 文件编码格式。 :return: """ try: if str(os.path.splitext(file_path)[-1]).lower() != 'csv': logger.warning("[WARNING]:生成的CSV文件扩展名必须为csv格式,请检查!") sys.exit(1) logger.info("[Initial]:开始通过Swagger转换生成CSV接口文档[依赖环境配置]......") swagger_dataframe = self.get_swagger_to_dataframe swagger_dataframe.to_csv(file_path, sep=sep, index=False, header=True, encoding=encoding) logger.info( '[Done]:已通过Swagger成功转换生成CSV接口文档,路径为"{}".'.format(file_path)) except Exception: logger.exception("[Exception]: 通过Swagger生成CSV接口文档失败,请检查原因!") sys.exit(1)
def send(self, message): """ 发送钉钉机器人消息提醒 :param message: 消息提醒 :return: """ try: if not isinstance(message, dict): logger.warning("[WARNING]:参数message必须以字典形式入参,请检查!") sys.exit(1) if not message.__contains__('msgtype'): logger.warning("[WARNING]:消息体message必须包含消息类型msgtype,请检查!") sys.exit(1) if message.get('msgtype') not in [ 'text', 'link', 'markdown', 'actionCard', 'feedCard' ]: logger.warning( '[WARNING]:检测到非法消息类型"{}",当前仅支持text、link、markdown、actionCard、feedCard,请重新指定!' .format(message.get('msgtype'))) sys.exit(1) url = "https://oapi.dingtalk.com/robot/send?access_token={}×tamp={}&sign={}".format( self.token, self.timestamp, self.get_sign) headers = {'Content-Type': 'application/json'} response = requests.request("POST", url, headers=headers, data=json.dumps(message)) result = response.json() if result.get("errcode") == 0 and result.get("errmsg") == 'ok': logger.info("[Done]:『{}』钉钉机器人消息提醒成功.".format(self.alias)) else: logger.warning( "[WARNING]:『{}』钉钉机器人消息提醒失败,接口响应为【{}】,开始重试...".format( self.alias, result)) sys.exit(1) except Exception: logger.exception("[Exception]:『{}』发送钉钉机器人消息提醒过程中发生异常,请检查!".format( self.alias)) sys.exit(1)
def check_duplicate_urlpath(self): """ 检查Swagger接口文档及接口抓包文档中是否存在重复的UrlPath. :return: """ pass_flag = False try: if self.check_empty_urlpath is True: swagger_doc_urlpath_veriry = { i: j for i, j in dict( Counter([ str(i).strip() for i in self.swagger_doc_df["UrlPath"].values.tolist() ])).items() if j > 1 } capture_doc_urlpath_veriry = { i: j for i, j in dict( Counter([ str(i).strip() for i in self.capture_doc_df["CID"].values.tolist() ])).items() if j > 1 } if swagger_doc_urlpath_veriry == {} and capture_doc_urlpath_veriry == {}: logger.info( "[Success]:Swagger接口文档或接口抓包文档中各自UrlPath列是否存在重复值检测通过,二者各自UrlPath列均无重复值." ) return True if swagger_doc_urlpath_veriry != {}: logger.warning( '[WARNING]:检测到Swagger接口文档中存在重复的UrlPath,检测结果:{},请仔细核对并明确需求!' .format(swagger_doc_urlpath_veriry)) pass_flag = False if capture_doc_urlpath_veriry != {}: logger.warning( '[WARNING]:检测到接口抓包文档中存在重复的UrlPath,检测结果:{},请仔细核对并明确需求!'. format(capture_doc_urlpath_veriry)) pass_flag = False return pass_flag else: logger.warning("[WARNING]:请先正常初始化Swagger接口文档和接口抓包文档!") sys.exit(1) except Exception: logger.exception( "[Exception]:检测Swagger接口文档或接口抓包文档中各自UrlPath列是否存在重复值过程中发生异常,请检查!" ) return False
def get_group_data(self, id=[], group=[], order=[], check_active=False): """ 获取分组修饰的数据。 :param id: 用例编号列表,如["TC_1", "TC_2"], 开启id参数后,分组group及序号order不生效。 :param group: 分组名称列表,如["A", "B"] :param order: 用例顺序列表,如[1, 2] :param check_active: 校验开关 :return: """ if not isinstance(id, list): logger.warning('[WARNING]:用例编号参数id必须以列表方式入参,如["TC_1","TC_2"]方式!') import sys sys.exit(1) if not isinstance(group, list): logger.warning('[WARNING]:测试分组参数group必须以列表方式入参,如["A","B"]方式!') import sys sys.exit(1) if not isinstance(order, list): logger.warning('[WARNING]:用例顺序参数order必须以列表方式入参,如[1,2]方式!') import sys sys.exit(1) if id != [] and check_active is False: return self.test_case_df[self.test_case_df["ID"].isin(id)] if id != [] and check_active is True: return self.test_case_df[(self.test_case_df["Active"] == True) & (self.test_case_df["ID"].isin(id))] if id == [] and group == [] and order == [] and check_active is False: return self.get_all_data(check_active=False) if id == [] and group == [] and order == [] and check_active is True: return self.get_all_data(check_active=True) if id == [] and group != [] and order == [] and check_active is False: return self.test_case_df[self.test_case_df["Group"].isin(group)] if id == [] and group != [] and order == [] and check_active is True: return self.test_case_df[(self.test_case_df["Active"] == True) & (self.test_case_df["Group"].isin(group))] if id == [] and group == [] and order != [] and check_active is False: return self.test_case_df[self.test_case_df["Order"].isin(order)] if id == [] and group == [] and order != [] and check_active is True: return self.test_case_df[(self.test_case_df["Active"] == True) & (self.test_case_df["Order"].isin(order))] if id == [] and group != [] and order != [] and check_active is False: return self.test_case_df[(self.test_case_df["Group"].isin(group)) & (self.test_case_df["Order"].isin(order))] if id == [] and group != [] and order != [] and check_active is True: return self.test_case_df[(self.test_case_df["Active"] == True) & (self.test_case_df["Group"].isin(group)) & (self.test_case_df["Order"].isin(order))]
def page_frequency(self, es_index, es_dsl): """ 计算ELK查询分页频次(size:10000条) :param es_index: ES索引 :param es_dsl: ES查询语句 :return: """ es_res = self.dsl_search(es_index, es_dsl) if es_res: hits_count_list = jsonpath(es_res.json(), "$.hits.total.value") if hits_count_list: hits_count = hits_count_list[0] if hits_count == 0: logger.warning("【ELK】DSL查询结果为空,请知悉!") sys.exit(1) logger.info("【ELK】DSL查询匹配结果总量为:{}".format(hits_count)) pages = divmod(hits_count, 10000) total_page = pages[0] leave_page = pages[1] if total_page == 0: logger.info("【ELK】已计算数据采集预期DSL分页频次为:1") return dict(count=hits_count, frequency=1, total_page=1, leave_page=0) elif total_page > 0 and leave_page == 0: logger.info("【ELK】已计算数据采集预期DSL分页频次为:{}".format(total_page)) return dict(count=hits_count, frequency=total_page, total_page=total_page, leave_page=0) elif total_page > 0 and leave_page > 0: logger.info("【ELK】已计算数据采集预期DSL分页频次为:{}".format(total_page + 1)) return dict(count=hits_count, frequency=total_page + 1, total_page=total_page, leave_page=1) else: logger.warning("【ELK】未成功计算预期DSL分页频次,请检查!") sys.exit(1) else: logger.error("【ELK】查询匹配结果总量获取失败,请检查查询结果或JsonPath表达式是否合法!") sys.exit(1) else: logger.warning("【ELK】由于DSL查询失败,导致结果总量未能获取!") sys.exit(1)