def parse_init_datas(self): """ 解析初始化sheet 1. 序列化testcase,以字典格式存放 :return: """ init_cases = [] init_cases_serialize = [] sheet = self.all_sheets.get('initialize_datas') if not sheet: log.info('testcase文件没有"数据初始化"sheet') self.init_case = [] return None rows = self._read_sheet_rows(sheet) rows = list(rows) for row in rows[2:]: if self.is_valid_case(row): init_cases.append( self._read_case_content(row, sheet_type="init")) log.info( string.Template("initialize test case info: $init_case"). substitute(init_case=init_cases)) for case in init_cases: init_cases_serialize.append(self._case_format(case)) self.init_case = init_cases_serialize
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 parse_restore_datas(self): """ 解析restore sheet 1. 序列化testcase,以字典格式存放 :return: """ restore_cases = [] restore_case_serialize = [] sheet = self.all_sheets.get('restore') if not sheet: log.info('testcase文件没有"数据恢复"sheet') self.restore_case = [] return None rows = self._read_sheet_rows(sheet) rows = list(rows) for row in rows[2:]: if self.is_valid_case(row): restore_cases.append( self._read_case_content(row, sheet_type="restore")) log.info( string.Template("restore test case info: $restore_case"). substitute(restore_case=restore_cases)) for case in restore_cases: restore_case_serialize.append(self._case_format(case)) self.restore_case = restore_case_serialize
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 send_text(self, msg, ats=None): """ func: 发送钉钉群消息 :param msg: text message conntent :param ats: phone list of to @, element "all" equal to @all :return: """ timestamp, sign = self._secret() url_ = self.token url_ = url_ + "×tamp={}&sign={}".format(timestamp, sign) log.debug('dingrobot complete url is: {}'.format(url_)) if ats is None: ats = [] text_msg = {"msgtype": "text", "text": {"content": msg}} at_info = {"atMobiles": [], "isAtAll": False} for item in ats: if item == "all": at_info["isAtAll"] = True elif isinstance(item, int): at_info["atMobiles"].append(item) else: pass text_msg["at"] = at_info header = {"Content-Type": "application/json"} with requests.post(url_, json.dumps(text_msg), headers=header) as res: log.info('dingrobot response is: {}'.format(res.text))
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 __init__(self, *args, **kwargs): self.api_infos = [] # log.debug(Template("**************$k").substitute(k=kwargs)) if kwargs.get("file"): log.info("处理文件api调试接口") self._file_parse(kwargs.get('file')) else: log.info("处理命令行参数api调试接口") self._params_parse(**kwargs)
def parse_testsuite_fields_lines(self, content): """ func: 解析测试sheet中局部变量、初始化数据、接口测试、数据恢复所在的行位置 :param content: sheet所有行的数据 :return: 局部变量、初始化数据、接口测试、数据恢复所在的行位置 """ lines_info = {} lines_info_result = {} if not isinstance(content, list): raise TypeError("params is not a list object") print(content) for num in range(0, len(content)): if '局部变量' == content[num][0].value: lines_info[self.testsuite_local_vars_line_name] = num if '初始化数据' == content[num][0].value: lines_info[self.testsuite_init_line_name] = num elif '接口测试' == content[num][0].value: lines_info[self.testsuites_testcase_line_name] = num elif '数据恢复' == content[num][0].value: lines_info[self.testsuites_restore_line_name] = num log.info( string.Template("line info: $lines_info").substitute( lines_info=lines_info)) lines_info_order = sorted(lines_info.items(), key=lambda x: x[1], reverse=False) fields_num = len(lines_info_order) if fields_num == 4: lines_info_result[lines_info_order[0][0]] = ( lines_info_order[0][1], lines_info_order[1][1]) lines_info_result[lines_info_order[1][0]] = ( lines_info_order[1][1], lines_info_order[2][1]) lines_info_result[lines_info_order[2][0]] = ( lines_info_order[2][1], lines_info_order[3][1]) lines_info_result[lines_info_order[3][0]] = ( lines_info_order[3][1], len(content)) if fields_num == 3: lines_info_result[lines_info_order[0][0]] = ( lines_info_order[0][1], lines_info_order[1][1]) lines_info_result[lines_info_order[1][0]] = ( lines_info_order[1][1], lines_info_order[2][1]) lines_info_result[lines_info_order[2][0]] = ( lines_info_order[2][1], len(content)) elif fields_num == 2: lines_info_result[lines_info_order[0][0]] = ( lines_info_order[0][1], lines_info_order[1][1]) lines_info_result[lines_info_order[1][0]] = ( lines_info_order[1][1], len(content)) elif fields_num == 1: lines_info_result[lines_info_order[0][0]] = ( lines_info_order[0][1], len(content)) log.debug( string.Template("lines_info_result: $lines_info_result"). substitute(lines_info_result=lines_info_result)) return lines_info_result
def generate_common_test(self): py_code = "" py_code += self._create_common_header() py_code += self._create_common_init_case_code(self.init_case) py_code += self._create_common_restore_class_code() py_code += self._create_common_restore_case_code(self.restore_case) with open(os.path.join(self.testcase_dir, "test_task_common.py"), 'w', encoding='utf-8') as f: f.write(py_code) log.info(Template("create common test files complete").substitute())
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 _read_file(self): """ 读取Excel文件,解析基础配置sheet,数据初始化sheet, 数据恢复sheet, 公共方法sheet和所有测试套sheet :return: """ all_sheets = {} test_suite = [] try: workbook = xlrd.open_workbook(self.filepath) except Exception as e: log.exception( string.Template("open testcase file fail, please check! %e"). substitute(e=e)) return False sheet_names = workbook.sheet_names() log.debug( string.Template("testcase sheets: $sheet_names").substitute( sheet_names=sheet_names)) # print(sheet_names) if '基础配置' in sheet_names: base_configs = workbook.sheet_by_name('基础配置') all_sheets['base_configs'] = base_configs sheet_names.remove('基础配置') if '数据初始化' in sheet_names: initialize_datas = workbook.sheet_by_name('数据初始化') all_sheets['initialize_datas'] = initialize_datas sheet_names.remove('数据初始化') if '数据恢复' in sheet_names: restore = workbook.sheet_by_name('数据恢复') all_sheets['restore'] = restore sheet_names.remove('数据恢复') if '公共方法' in sheet_names: common_func = workbook.sheet_by_name('公共方法') all_sheets['common_func'] = common_func sheet_names.remove('公共方法') if '全局变量' in sheet_names: global_vars = workbook.sheet_by_name('全局变量') all_sheets['global_vars'] = global_vars sheet_names.remove('全局变量') for sheet in sheet_names: test_suite.append(workbook.sheet_by_name(sheet)) log.info( string.Template( "All the test suite that need to be tested: $test_suite"). substitute(test_suite=test_suite)) all_sheets['testsuites'] = test_suite return all_sheets
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 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 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 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 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 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
def header_serialize(self): header = self.api_info['header'].strip().replace( '\n', '') if self.api_info.get("header") else None log.info( string.Template("api request header info is: $header").substitute( header=header)) if not header: return res = self._json_serialize(header) if res: self.api_info['header'] = res return res else: log.error( string.Template( "api request header format error, header info: $header"). substitute(header=self.api_info['header'])) return False
def case_vars_serialize(self): case_vars = self.api_info['case_vars'].strip().replace( '\n', '') if self.api_info.get("case_vars") else None log.info( string.Template("api request case_vars info is: $case_vars"). substitute(case_vars=case_vars)) if not case_vars: return res = self._json_serialize(case_vars) if res: self.api_info['case_vars'] = res return res else: log.error( string.Template( "api request case_vars format error, case_vars info: $case_vars" ).substitute(_vars=self.api_info['case_vars'])) return False
def method_serialize(self): method = self.api_info['method'].strip().replace( '\n', '') if self.api_info.get('method') else None log.info( string.Template("api info method info is: $method").substitute( method=method)) supported = ['get', 'post', 'delete', 'put'] if not method: log.error( string.Template("method params are required").substitute()) raise JsonSerializeException elif method.lower() not in supported: log.error( string.Template("method params only support $s").substitute( s=supported)) raise JsonSerializeException else: self.api_info['method'] = method.upper() return method.upper()
def params_serialize(self): # params = self.api_info['params'].strip().replace('\n', '') params = self.api_info['params'].strip().replace( '\n', '') if self.api_info.get("params") else None log.info( string.Template("api request params info is: $params").substitute( params=params)) if not params: return res = self._json_serialize(params) if res: self.api_info['params'] = res return res else: log.error( string.Template( "api request params format error, params info: $params"). substitute(params=self.api_info['params'])) return False
def generate_testsuite(self): for sheet_name, testsuites in self.testsuites.items(): if not testsuites: continue py_code = "" py_code += self._create_header_code() py_code += self._create_save_interface_vars_code() py_code += self._create_setup_class_case_code( testsuites['init_case']) py_code += self._create_teardown_class_case_code( testsuites['restore_case']) py_code += self._create_testcase_code(testsuites['testcase']) py_code += self._create_testcase_common_func_code() with open(os.path.join(self.testcase_dir, "test_{}.py".format(sheet_name)), 'w', encoding='utf-8') as f: f.write(py_code) log.info(Template("create testsuite files complete").substitute())
def open_report_server(self, report_dir, ip=None, port=None): if not port: port = 55555 suffix_dir = os.path.split(report_dir)[0] relative_path = os.path.split(report_dir)[1] self.trend_detail(report_dir) os.chdir(suffix_dir) self.check_port_is_open(port) generate_cmd = "allure generate {} -o {} -c".format( relative_path, self.allure_report) if ip: open_server_cmd = "allure open {} -h {} -p {}".format( self.allure_report, ip, port) else: open_server_cmd = "allure open {} -p {}".format( self.allure_report, port) obj = subprocess.Popen(generate_cmd, shell=True) obj.communicate() obj = subprocess.Popen(open_server_cmd, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) couter = 0 for item in iter(obj.stdout.readline, 'b'): couter += 1 encode_type = chardet.detect(item) if encode_type['encoding'] == 'utf-8': item = item.decode('utf-8') elif encode_type['encoding'] == 'Windows-1252': item = item.decode('Window-1252') else: pass log.debug('subprocess out is: {}'.format(str(item))) pattern = re.compile(r'Server started at <(.+?)>\.') rs = re.search(pattern, str(item)) if rs: url = rs.group(1) log.info('reports server is: {}'.format(url)) return url time.sleep(0.5) if couter > 100: return "获取allure报告服务地址失败"