def init_serialize(self): init = self.api_info['init'].strip().replace( '\n', '') if self.api_info.get('init') else None log.info( string.Template("api request init info is: $init").substitute( init=init)) if not init: return res = self._json_serialize(init) srlz_res = [] if res: for case in res: sr = Serialize(case) sr.url_prepare() sr.method_check() sr.wait_time_value_check() srlz_res.append(sr.api_info) self.api_info['init'] = srlz_res return srlz_res else: log.error( string.Template( "api request init format error, init info: $init"). substitute(init=self.api_info['init'])) return False
def append_row(self, sheet_name, data_list, passed): """ 往Excel表格中追加数据 :param sheet_name: :param data_list: :param passed 用例是否执行通过 :return: """ passed = passed or 'PASS' if not isinstance(data_list, list): log.error("要写入的结果的入参类型错误") return False if sheet_name not in self.sheet_info: self.sheet = self.workbook.add_sheet(sheet_name, cell_overwrite_ok=True) self.sheet_info[sheet_name] = {"row": 1, "col": 0} self.write_row(sheet_name, 0, 0, self.sheet_title) else: self.sheet = self.workbook.get_sheet(sheet_name) row = self.sheet_info[sheet_name]['row'] col = self.sheet_info[sheet_name]['col'] if passed.upper() == 'PASS': style = self.set_style('green') elif passed.upper() == 'FAIL': style = self.set_style('red') elif passed.upper() == 'WARNING': style = self.set_style('yellow') else: style = self.set_style('green') try: self.write_row(sheet_name, row, col, data_list, style) except Exception as e: log.error(Template("往Excel表格中写入数据异常,异常信息:$e").substitute(e=e)) self.sheet_info[sheet_name]['row'] += 1
def smtp_link(self): """ func: 连接smtp服务器,判断当前所在的平台,连接smtp服务 :return: """ if "win" in sys.platform.lower(): log.info('mail server in windows platform') smtp = smtplib.SMTP_SSL(self.server) elif "linux" in sys.platform.lower(): log.info('mail server in linux platform') smtp = smtplib.SMTP_SSL(self.server) else: log.error('mail server not support this system platform') raise SystemError('mail server not support this system platform') try: smtp.connect(self.server, self.port) log.info('smtp connect server success') smtp.login(self.sender, self.pwd) log.info('login mail server success') self.smtp_handler = smtp return smtp except Exception as e: log.error('login mail server fail, error info is: {}'.format(e)) return False
def append_row(self, sheet_name, data_list, passed): """ 往Excel表格中追加数据 :param sheet_name: :param data_list: :param passed 用例是否执行通过 :return: """ passed = passed or 'PASS' if not isinstance(data_list, list): log.error("要写入的结果的入参类型错误") return False if sheet_name not in self.sheet_info: self.append(sheet_name, self.sheet_title) else: self.sheet = self.workbook.get_sheet(sheet_name) if passed.upper() == 'PASS': style = self.set_style('green') elif passed.upper() == 'FAIL': style = self.set_style('red') elif passed.upper() == 'WARNING': style = self.set_style('yellow') else: style = self.set_style('green') self.append(sheet_name, data_list, style)
def restore_serialize(self): restore = self.api_info['restore'].strip().replace( '\n', '') if self.api_info.get('restore') else None log.info( string.Template("api request restore info is: $restore"). substitute(restore=restore)) if not restore: return res = self._json_serialize(restore) srlz_res = [] if res: for case in res: sr = Serialize(case) sr.url_prepare() sr.method_check() sr.wait_time_value_check() srlz_res.append(sr.api_info) self.api_info['restore'] = srlz_res return srlz_res else: log.error( string.Template( "api request restore format error, restore info: $restore" ).substitute(restore=self.api_info['restore'])) return False
def check_response_header(fields, headers): error_info = "" if not fields: return error_info log.debug( Template("fields: $fields, headers: $headers").substitute( fields=fields, headers=headers)) if not fields: return error_info if isinstance(fields, str): log.debug('is a string fields, value is:'.format(fields)) fs = fields.split(',') elif isinstance(fields, list): log.debug('is a list fields, value is: '.format(str(fields))) fs = fields else: log.error('fields param type error') raise TypeError('fields param type error') for text in fs: text = text.lstrip("'").lstrip('"').rstrip("'").rstrip('"') if text: if isinstance(headers, dict): # if not headers.get(text): isempty = jmespath.search(text, headers) if isempty is None: error_info += Template( "接口响应内容没有待校验的字段:$t,响应内容:$res").substitute(t=text, res=headers) else: if text not in headers: error_info += Template( "接口响应内容没有待校验的字段:$t,响应内容:$res").substitute(t=text, res=headers) return error_info
def check_response_text(fields, response): error_info = "" log.debug( Template("fields: $fields, response: $response").substitute( fields=fields, response=response)) if not fields: return error_info if isinstance(fields, str): log.debug('is a string fields, value is:'.format(fields)) fs = fields.split(',') elif isinstance(fields, list): log.debug('is a list fields, value is: '.format(str(fields))) fs = fields else: log.error('fields param type error') return 'check res_text error, fields param type error' for text in fs: text = text.lstrip("'").lstrip('"').rstrip("'").rstrip('"') if text: log.debug(Template("待校验的内容: $text").substitute(text=text)) if isinstance(response, dict): isempty = jmespath.search(text, response) if isempty is None: error_info += Template( "接口响应内容没有待校验的字段:$t,响应内容:$res").substitute(t=text, res=response) else: if text not in response: error_info += Template( "接口响应内容没有待校验的字段:$t,响应内容:$res").substitute(t=text, res=response) return error_info
def _order_for_testcase(self, cases, base_num): """ func:用于给所有测试用例编排执行顺序 :param cases: :return: """ orders = self._get_cases_orders(cases) log.debug( string.Template("**************$o****************").substitute( o=orders)) max_num = max(orders) + 1 if orders else 1 ordered = [] for case in cases: try: order = int(case['order']) if case['order'] else None except Exception as e: log.error( string.Template( "integer order number error, error info: $e"). substitute(e=e)) order = None if order: case['order'] = base_num + order else: case['order'] = base_num + max_num max_num += 1 ordered.append(case) return ordered
def __init__(self, filepath): if not os.path.isfile(filepath): log.error( string.Template( "test case file not found, please check! $filepath"). substitute(filepath=filepath)) raise FileNotFoundError("test case file not found, please check!") self.filepath = filepath self.excel_fields = settings.excel_fields self.init_case = None # 数据初始化sheet self.restore_case = None # 数据恢复sheet self.base_configs = None # 基础配置sheet self.all_sheets = None # Excel文件所有sheet self.testsuites = {} # 除了已有固定的sheet外的其他测试sheet self.testsuite_local_vars_line_name = "local_line" self.testsuite_init_line_name = "init_line" self.testsuites_restore_line_name = "restore_line" self.testsuites_testcase_line_name = "testcase_line" self._read_file() self.parse_base_configs() self.parse_global_vars() self.parse_init_datas() self.parse_testsuite() self.parse_restore_datas() self.cases_order()
def trend_detail(self, report_dir): log.info('处理allure测试报告trend内容') if not os.path.isdir(report_dir): log.error('需要处理trend的目录不是一个有效路径,请检查') return False allure_json_file_dirs = [] # 所有allure json报告的所有目录 suffix_dir = os.path.split(report_dir)[0] # 测试报告路径前缀 relative_path = os.path.split(report_dir)[1] # 当前测试报告目录 for item in os.listdir(suffix_dir): if re.match(r'^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}', item): allure_json_file_dirs.append(item) allure_json_file_dirs = sorted(allure_json_file_dirs, reverse=True) log.info( 'all the allure report dirs is: {}'.format(allure_json_file_dirs)) last_allure_report_dir = os.path.join( suffix_dir, self.allure_report) # 最近一次allure json报告的目录 log.info( 'last_allure_report_dir is: {}'.format(last_allure_report_dir)) if not os.path.isdir(last_allure_report_dir): log.info('no last_allure_report_dir, no need to detail trend') return None else: last_allure_report_history_dir = os.path.join( last_allure_report_dir, "history") # 最近一次allure_report目录中的history目录 last_allure_json_dir = os.path.join( suffix_dir, allure_json_file_dirs[0]) # 最近一次allure json文件目录 last_history_dir = os.path.join( last_allure_json_dir, "history") #最近一次allure json文件目录中history目录 self.manual_join_trend_file(last_allure_report_history_dir, last_history_dir, report_dir)
def build_connection(self): try: self.database = pymysql.connect(**self.cfg) log.info('connect mysql success') return self.database except Exception as e: log.error(Template('connect mysql fail, the error info: $e, the database config info: $cfg' '').substitute(e=e, cfg=self.cfg))
def _json_serialize(cls, s): try: # s = json.dumps(s) return ast.literal_eval(s) except Exception as e: log.error( string.Template( "string serialize json fail, source string: $s, error info: $e" ).substitute(s=s, e=e)) raise JsonSerializeException("serialize to JSON format error")
def db_verify_serialize(self): """ func:对测试用例中的db_verify项进行json格式化和基本字段校验 首先判断db_verify字段是否符合json格式,如果符合,则该字段进行sql语句判断有无,db配置是否在基础配置中已配置,检查的类型是否正确 能检查的类型为:= or == 表示查询的值和预期值要相等,~ or ~=表示预期值包含在查询结果中,! or != 表示查询值不能等于预期值, ~ or !~ 表示预期值不能包含在查询值中 :return:json格式化之后的db_verify字段 """ db_verify = self.api_info['db_verify'].strip() if self.api_info.get( 'db_verify') else None log.info( string.Template("api request db_verify info is: $db_verify"). substitute(db_verify=db_verify)) if not db_verify: return res = self._json_serialize(db_verify) if res: flag = True databases_list = Process.base_configs.get('database') or {} type_list = ["=", '==', '!', '!=', '~', '~=', '!~', '~!'] error_msg = "" for k, v in res.items(): sql_ = v.get('sql') db = v.get('db') type_ = v.get('type') or '=' if not sql_: flag = False error_msg += "{} sql键不能为空,请检查\n".format(k) if not db or db not in databases_list: flag = False error_msg += "{} 配置的db选项没有在基础配置中配置相关的参数\n".format(k) else: v['ip'] = databases_list[db].get('ip') v['port'] = databases_list[db].get('port') v['db_type'] = databases_list[db].get('type') v['user'] = databases_list[db].get('user') v['pwd'] = databases_list[db].get('pwd') if type_ not in type_list: flag = False error_msg += "{} 配置的type目前无法识别,请检查type选项是否以下选项:{}\n".format( k, type_list) res[k] = v if not flag: log.error(error_msg) raise JsonSerializeException self.api_info['db_verify'] = res return res else: log.error( string.Template( "api request db_verify format error, db_verify info: $db_verify" ).substitute(db_verify=self.api_info['db_verify'])) return False
def query(self, sql): with self.__conn.cursor() as cursor: log.info('query sql is: {}'.format(sql)) try: cursor.execute(sql) # data_one = cursor.fetchone() data_all = cursor.fetchall() log.debug(Template('query results is: $all').substitute(all=data_all)) return data_all except Exception as e: log.error('query fail, error info: {}, the sql is: {}'.format(str(e), str(sql))) return False
def parse_interface_var(source: object, response: object) -> object: var_dict = {} for k, v in source.items(): try: var_dict[k] = jmespath.search(v, response) except Exception as e: log.error( string.Template( "get interface_var:$v from response fail,error info: $e"). substitute(v=v, e=e)) return var_dict
def _read_sheet_rows(cls, sheet): """ 获取整个sheet表的所有行数据 :param sheet: :return: 所有行的数据 """ try: return sheet.get_rows() except Exception as e: log.error( string.Template("get sheet rows fail, sheet is: $sheet"). substitute(sheet=sheet.name)) log.exception(e)
def _testcase_dir_init(self): if os.path.isdir(self.testcase_dir): try: shutil.rmtree(self.testcase_dir) except Exception as e: log.error( Template("testcase目录无法删除,请检查是否有其他应用正在使用该目录下的资源,错误信息:$e"). substitute(e=e)) return False os.makedirs(self.testcase_dir) with open(os.path.join(self.testcase_dir, "__init__.py"), 'w', encoding='utf-8') as f: f.write("# -*- coding: utf-8 -*-")
def url_prepare(self): url = self.api_info['url'].strip().replace( '\n', '') if self.api_info.get('url') else None log.info( string.Template("api info url info is: $url").substitute(url=url)) if not url: log.error(string.Template("url params are required").substitute()) raise JsonSerializeException elif not url.startswith('http://'): res = Process.base_configs['hostname'] + url self.api_info['url'] = res return res else: return url
def check_status_code(code, response): error_info = "" if not code: return error_info try: code = int(code) except Exception as e: log.error(Template("接口响应状态码入参错误,请检查, 入参值:$code").substitute(code=code)) error_info += Template("接口响应状态码入参错误,请检查, 入参值:$code").substitute( code=code) return error_info if code != response: error_info += Template("接口响应码校验失败:预期值:$n,响应值:$res").substitute( n=code, res=response) return error_info
def send(self, mail_body, receivers, attachs=None, subject="auto-test mail notify"): """ func: 发送邮件功能 :param mail_body: 邮件文本主体,字符串类型 :param receivers: 接收者列表,list类型 :param attachs: 需要添加的附件,list类型,list中的每个元素需要是dict类型,并且需要有filename和filepath两个属性 :param subject: 邮件主题 :return: """ if not attachs: if not isinstance(attachs, list): log.error('attach param is not a list') attach = None msg = MIMEMultipart() # 支持邮件中附带附件 msg['Subject'] = Header(subject, 'utf-8') msg['From'] = Header(self.sender, 'utf-8') for receiver in receivers: msg['To'] = Header(receiver, 'utf-8') mail_body = "(本邮件是程序自动下发的,请勿回复!)\r\n" + mail_body text_content = MIMEText(mail_body, 'plain', 'utf-8') msg.attach(text_content) # 添加附件 if attachs: attachs = self.attach_check(attachs) for att in attachs: filepath = att.get('filepath') filename = att.get('filename') log.debug('filepath is: {}'.format(filepath)) att_tmp = MIMEText( open(filepath, 'rb').read(), 'base64', 'utf-8') att_tmp['Content-type'] = 'application/octet-stream' att_tmp[ 'Content-Disposition'] = 'attachment; filename="{}"'.format( filename) msg.attach(att_tmp) try: if self.smtp_link(): self.smtp_handler.sendmail(self.sender, receivers, msg.as_string()) log.info("send mail success!") except Exception as e: log.error("发送邮件异常, 错误信息:{}".format(e))
def dyparam_parse(testcase: dict, var_object: object): """ func: 根据dyparam里的sql信息,进行数据库查询,将查询到的结果存入var_object对象的interface_vars属性中 :param dyparam: 测试用例中的dyparam信息经过json转换后的内容 :param var_object: 用于存放测试sheet接口变量的对象 :return: 添加了从数据库中查询出来的值之后的var_object对象 """ if not isinstance(testcase, dict): return "testcase is not a dict" dyparam = testcase.get('dyparam') if not dyparam: return None if not isinstance(dyparam, dict): return "dyparam is not a dict type" if not hasattr(var_object, "interface_vars"): var_object.interface_vars = {} log.info('进入dyparam解析') for name_, sql_info in dyparam.items(): ip = sql_info.get('ip') port = sql_info.get('port') pwd = sql_info.get('pwd') user = sql_info.get('user') db_type = sql_info.get('db_type') values = sql_info.get('values') sql = sql_info.get('sql') rs = DbHelper().query(ip=ip, port=port, user=user, pwd=pwd, db_type=db_type, sql=sql) if not rs: log.warning("sql: {} 查询结果为空".format(sql)) for var_name, reslut_key in values.items(): var_object.interface_vars[var_name] = "" continue rs = rs[0] try: for var_name, reslut_key in values.items(): log.info('添加interface_var: {}={}'.format( var_name, rs.get(reslut_key))) var_object.interface_vars[var_name] = rs.get(reslut_key) except Exception as e: log.error('add interface_vars fail, error info is: {}'.format(e)) return False return var_object
def parse_interface_var(source: object, response: object) -> object: var_dict = {} if not isinstance(source, dict): return 'response is not a dict' for k, v in source.items(): try: res = jmespath.search(v, response) if isinstance(res, list): var_dict[k] = res[0] else: var_dict[k] = res except Exception as e: log.error( string.Template( "get interface_var:$v from response fail,error info: $e"). substitute(v=v, e=e)) return var_dict
def order_fields_check(self): order = self.api_info['order'] if self.api_info.get("order") else None log.info( string.Template("api request order info is: $order").substitute( order=order)) if not order: return None try: res = int(order) self.api_info['order'] = res return res except Exception as e: log.error( string.Template( "api order param format error, please input a integer number " "error info: $e").substitute(e=e)) raise JsonSerializeException
def check_response_time(_time, response): error_info = "" if not _time: return error_info try: _time = float(_time) if _time < response: log.error( Template("接口响应时间超时,期望值: $t, 响应值: $r").substitute(t=_time, r=response)) error_info += Template("接口响应时间超时,期望值: $t, 响应值: $r").substitute( t=_time, r=response) return error_info except Exception as e: log.error(Template("接口响应时间入参错误,请检查: $t").substitute(t=_time)) error_info += Template("接口响应时间入参错误,请检查: $t").substitute(t=_time) return error_info
def dyparam_serialize(self): dyparam = self.api_info['dyparam'].strip().replace( '\n', '') if self.api_info.get('dyparam') else None log.info( string.Template("api request dyparam info is: $dyparam"). substitute(dyparam=dyparam)) if not dyparam: return res = self._json_serialize(dyparam) if res: self.api_info['dyparam'] = res return res else: log.error( string.Template( "api request dyparam format error, dyparam info: $dyparam" ).substitute(dyparam=self.api_info['dyparam'])) return False
def restore_serialize(self): restore = self.api_info['restore'].strip().replace( '\n', '') if self.api_info.get('restore') else None log.info( string.Template("api request restore info is: $restore"). substitute(restore=restore)) if not restore: return res = self._json_serialize(restore) if res: self.api_info['restore'] = res return res else: log.error( string.Template( "api request restore format error, restore info: $restore" ).substitute(restore=self.api_info['restore'])) return False
def init_serialize(self): init = self.api_info['init'].strip().replace( '\n', '') if self.api_info.get('init') else None log.info( string.Template("api request init info is: $init").substitute( init=init)) if not init: return res = self._json_serialize(init) if res: self.api_info['init'] = res return res else: log.error( string.Template( "api request init format error, init info: $init"). substitute(init=self.api_info['init'])) return False
def expression_serialize(self): expression = self.api_info['expression'].strip().replace( '\n', '') if self.api_info.get('expression') else None log.info( string.Template("api request expression info is: $expression"). substitute(expression=expression)) if not expression: return res = self._json_serialize(expression) if res: self.api_info['expression'] = res return res else: log.error( string.Template( "api request expression format error, expression info: $expression" ).substitute(expression=self.api_info['expression'])) return False
def verify_fields_serialize(self): verify = self.api_info['verify_fields'].strip().replace( '\n', '') if self.api_info.get('verify_fields') else None log.info( string.Template("api request verify_fields info is: $verify"). substitute(verify=verify)) if not verify: return res = self._json_serialize(verify) if res: self.api_info['verify_fields'] = res return res else: log.error( string.Template( "api request verify_fields format error, verify_fields info: $verify_fields" ).substitute(verify_fields=self.api_info['verify_fields'])) return False
def interface_var_serialize(self): var = self.api_info['interface_var'].strip().replace( '\n', '') if self.api_info.get("interface_var") else None log.info( string.Template( "api request interface_var info is: $var").substitute(var=var)) if not var: return res = self._json_serialize(var) if res: self.api_info['interface_var'] = res return res else: log.error( string.Template( "api request interface_var format error, interface_var info: $var" ).substitute(var=self.api_info['interface_var'])) return False