def _send_IM(self): """ IM report 发送到IM群 如果需要自定义内容,可实现_custom_IM_body方法 """ LogUtil.info(u"[IM SEND BEGIN] 开始发送IM 群通知。。。") IM_body, pass_rate = IM_report.format_simple_summary( self.unittest_results) # 当前服务请求接口数量 covered_api_counter = self.__counter_current_project_covered_api() if covered_api_counter: IM_body += "\ncovered_api_counter : {}\n".format( covered_api_counter) # 自定义内容 custom_IM_body = self._custom_IM_body() final_IM_body = u"{}\n详细报告 :<a href=http://{}/auto_reports/{}/{}>Report Link</a> \n{}".format( IM_body, tomcat_server, self.project_name, self.report_file_name, custom_IM_body) title_time = time.strftime("%Y-%m-%d_%H:%M", time.localtime()) msg_title = u"{} 自动化测试报告_{} - 通过率 : {} %".format( self.project_name.upper(), title_time, pass_rate) self.IM_client.bot_join_group(self.group_id) self.IM_client.send_msg_to_group(msg_title, final_IM_body, self.group_id) for receiver in self.report_receiver_list: self.IM_client.send_msg_to_person(msg_title, final_IM_body, receiver)
def get_random_size_list(my_list, list_size=None): ''' 从一个list里面,生成随机大小的子集list; 比如: [2,5,7,9] 可能随机的结果是 [2,5], 也可能是[5,7,9] :param my_list: 原始list :param list_size: 期望拿到的输出的list 长度,必须要小于原始list长度 :return: ''' if list_size is not None: if list_size > len(my_list): raise Exception("期望得到的list_size,超过了当前list 最大值 - {}!!!".format( len(list))) how_many = list_size else: if len(my_list) < 1: LogUtil.error("当前应该有错误,需要看一下 this_list: {}".format( str(my_list))) raise Exception(u" 期望de this_list长度小于1!!!") how_many = random.randint(1, len(my_list)) tmp_list = deepcopy(my_list) final_list = [] for i in xrange(how_many): rand_item = tmp_list[random.randint(1, len(tmp_list)) - 1] final_list.append(rand_item) tmp_list.pop(tmp_list.index(rand_item)) return final_list
def send_IM(self, pass_rate, total, _pass, failure, error, merge_report_file_name): """ IM report 发送到IM群 如果需要自定义内容,可实现_custom_IM_body方法 """ LogUtil.info(u"[IM SEND BEGIN] 开始发送IM 群通知。。。") IM_body, pass_rate = IM_report.format_report_for_temporary_test( pass_rate=pass_rate, total_case=total, pass_case=_pass, failure_case=failure, error_case=error, ) # 自定义内容 custom_IM_body = self._custom_IM_body() final_IM_body = u"{}\n详细报告 :<a href=http://{}/auto_reports/{}/{}>Report Link</a> \n{}".format( IM_body, tomcat_server, self.project_name, merge_report_file_name, custom_IM_body) title_time = time.strftime("%Y-%m-%d_%H:%M", time.localtime()) msg_title = u"{} 自动化测试报告_{} - 通过率 : {} %".format( self.project_name.upper(), title_time, pass_rate) self.IM_client.send_msg_to_group(msg_title, final_IM_body, self.group_id) for receiver in self.report_receiver_list: self.IM_client.send_msg_to_person(msg_title, final_IM_body, receiver)
def _print_cover_request_url_set(self, request_url_set): LogUtil.info( u"\n-----------------------------------------------------------------------" ) LogUtil.info(u"【URL 覆盖】当前项目【{}】 覆盖的url,接口如下: ".format( self.project_name)) print('\n'.join(request_url_set))
def _gen_rpc_payload(self, func_name, params_body): body = {} body['rpc_server_name'] = self.rpc_server_name body['func_name'] = func_name body['request'] = json.dumps(params_body) if self.address: body['address'] = self.address LogUtil.print_dict_in_lines(body, u"rpc payload") return json.dumps(body)
def get_for_xxx(self, param_1, param_2): LogUtil.step(u"--- 通过API xxxx {} {}".format(param_1, param_2)) params = { "param_1": param_1, "param_2": param_2, } rsp = MyRequests.get(self.GET_API, params=params) return rsp.json().get('data')
def post_for_xxx(self, param_1, param_2): LogUtil.step(u"--- 通过API xxxx {} {}".format(param_1, param_2)) data = { "param_1": param_1, "param_2": param_2, } req = MyRequests.post(self.POST_API, data=data, is_json=True) return req.json().get('code'), req.json().get('msg'), req.json().get('data')
def _post(self): """ 后处理 用于处理数据,结果处理,展示等 这里暂时不留钩子 """ # 拷贝到远程 if self.is_scp_to_remote: self._scp_to_remote() else: LogUtil.info(u"[REMOTE] 指定不拷贝到远程!本地文件名不带时间戳。")
def get_email(): ''' 随机生成一个可用的邮箱 :return: ''' randNumb = random.randint(1, 9999999) mail = "auto.mail.{}@bytedance.com".format(randNumb) LogUtil.info(u"使用随机生成的email : " + str(mail)) return mail
def __generate_local_report_file_path(self): """ 生成本地文件路径,这里用的根路径加report """ local_report_file_full_path = os.path.abspath( os.path.join(test_env_config.ROOT_DIR, "report", self.report_file_name)) LogUtil.info("Report_name=" + self.report_file_name) LogUtil.info(u"报告存放位置: \n" + local_report_file_full_path) # 确定生成报告的本地全路径 self.local_report_file_full_path = local_report_file_full_path
def address(self): if self.rpc_host not in VALID_TEST_HOST: raise Exception( "测试环境可用机器列表: {}, 不包含 当前请求的rpc host: {}\n " " -- 如果有例外,local 使用时候,可以屏蔽这里\n" "['注意!!!']不可以把线上ip push上去,自动化干的事情,谁也保不齐!!!".format( VALID_TEST_HOST, self.rpc_host)) if self.rpc_port and self.rpc_port: return "{}:{}".format(self.rpc_host, self.rpc_port) else: LogUtil.error(u'本次没有指定rpc的IP和端口,本次请求会打到线上!') return
def test_1(self): """\ 说明\ <br> 步骤与校验\ <br> 1. 步骤 校验\ <br> 2. 步骤 校验\ <br> 3. 步骤 校验\ """ LogUtil.step(u"步骤1") param = self.db_cont_ops.get_first_info(1, 'name') LogUtil.verify(u"校验1") self.verify_1(param)
def _act(self): """ 执行case,产生结果 """ self._generate_var() LogUtil.info(u"[RUN BEGIN] 结果输出到本地路径: {}".format( self.local_report_file_full_path)) local_out_stream = open(self.local_report_file_full_path, 'wb') runner = HTMLTestRunner.HTMLTestRunner( stream=local_out_stream, title=self.report_title, description=self.report_description) self.unittest_results = runner.run(self.suite()) LogUtil.info(u"[RUN END] 结果输出到本地路径: {}".format( self.local_report_file_full_path))
def get_name(sale_id=None): ''' 随机生成一个名称,有Auto关键字 :param sale_id: 不填写没东西,如果填写了虚拟人员id,会拼写虚拟人员中文名 :return: ''' ticks = str(int(time.time())) # ran = str(random.randint(0, 50000)) if sale_id: name = people_dict.get_name_by_id(sale_id) else: name = u"名字" name = u"Auto-{}{}".format(name, ticks) LogUtil.info(u"随机生成的名称: {}".format(name)) return name
def post_after_merge(self, pass_rate, total, _pass, failure, error, merge_report_file_name): """ 合并后发送IM消息 """ # 拷贝到远程 if self.is_scp_to_remote: self.scp_merge_file_to_remote(merge_report_file_name) else: LogUtil.info(u"[REMOTE] 指定不拷贝到远程!本地文件名不带时间戳。") # 发送IM通知 if self.is_send_IM: self.send_IM(pass_rate=pass_rate, total=total, _pass=_pass, failure=failure, error=error, merge_report_file_name=merge_report_file_name)
def get_number_serial(length=None): ''' 随机生成一个数字序列号 返回 str 至少3位 ''' if length == None: return str(random.randint(19999999999999, 99999999999999)) elif length > 3: number_of_zero = length - 1 start_serial = "1{}".format("".rjust(number_of_zero, str('0'))) start_long = long(start_serial) end_serial = "1{}".format("".rjust(length, str('0'))) end_long = long(end_serial) - 1 randNumb = random.randint(start_long, end_long) else: raise Exception(u'[ERROR]目前只支持三维以上随机数生成!!!') LogUtil.info(u"使用随机生成的长传数字: " + str(randNumb)) return randNumb
def _record_performance(start, request_obj): """ 记录性能相关,如果超过阈值,记录下来请求内容 """ end = datetime.datetime.now() diff_seconds = (end - start).total_seconds() LogUtil.info(u"-----------------------------------------------") LogUtil.info(u"【性能】请求花费时间 {} s ".format(diff_seconds)) LogUtil.info(u"-----------------------------------------------")
def _print_request_cost_info(self, request_list): LogUtil.info( u"\n-----------------------------------------------------------------------" ) LogUtil.info(u"【性能】接口耗时,超过 '{}' s 的接口请求如下: ".format( cached_data.time_cost_threshold)) if len(request_list) == 0: LogUtil.info(u"棒棒的,没有任何请求超过阈值!") request_url_dict = {} for request in request_list: request_url_dict[request.get('request').get('url')] = request.get( 'time') DataFormatter().print_dict_in_lines(request_url_dict)
def _scp_to_remote(self): """ 拷贝到远程 """ server_location = "{}:{}{}/".format( tomcat_server.split(":")[0], server_tomcat_dir, self.project_name) scp_command = 'scp {} {}'.format(self.local_report_file_full_path, server_location) LogUtil.info(u"[SCP REMOTE BEGIN] 拷贝到远程 命令: {}".format(scp_command)) os.system(scp_command) LogUtil.info( u"[SCP REMOTE END] 拷贝完成 remote tomcat server : \n {}".format( scp_command)) remote_url_head = "http://{}/auto_reports/{}/".format( tomcat_server, self.project_name, self.report_file_name) remote_full_path = remote_url_head + self.report_file_name LogUtil.info(u"远端服务器URL: {}".format(remote_full_path))
def _post(self): """ 后处理 用于处理数据,结果处理,展示等 这里暂时不留钩子 """ # 拷贝到远程 if self.is_scp_to_remote: self._scp_to_remote() else: LogUtil.info(u"[REMOTE] 指定不拷贝到远程!本地文件名不带时间戳。") # 发送IM通知 if self.is_send_IM: self._send_IM() else: LogUtil.info(u"[IM] 指定不发送IM") LogUtil.info(u"最后再次打印报告存放位置: \n" + self.local_report_file_full_path) # 最后打印一下,性能可能超标的请求 set self._print_request_cost_info(cached_data.request_list) self._print_cover_request_url_set(cached_data.request_path_set)
def scp_merge_file_to_remote(self, merge_report_file_name): """ 拷贝到远程 """ server_location = "{}:{}{}/".format( tomcat_server.split(":")[0], server_tomcat_dir, self.project_name) import os local_report_file_full_path = os.path.abspath( os.path.join(test_env_config.ROOT_DIR, "report", merge_report_file_name)) scp_command = 'scp {} {}'.format(local_report_file_full_path, server_location) LogUtil.info(u"[SCP REMOTE BEGIN] 拷贝到远程 命令: {}".format(scp_command)) import os os.system(scp_command) LogUtil.info( u"[SCP REMOTE END] 拷贝完成 remote tomcat server : \n {}".format( scp_command)) remote_url_head = "http://{}/auto_reports/{}/".format( tomcat_server, self.project_name, merge_report_file_name) remote_full_path = remote_url_head + merge_report_file_name LogUtil.info(u"远端服务器URL: {}".format(remote_full_path))
def rpc_request(self, func_name, params_body): """ 通过MS测试通用接口调用rpc接口获取结果 :param func_name: rpc方法名 不带括号,不带参数描述,支持下划线形式 如 query_a_b_c :param params_body: dict 该方法req的 params map :return: 调用成功的结果 """ LogUtil.info(u"-----------------【rpc Request】 ----------------") LogUtil.info(u" 基础参数 rpc_server_name: {}, address: {}".format(self.rpc_server_name, self.address)) payload = self._gen_rpc_payload(func_name, params_body) start = datetime.datetime.now() errno, data, msg = rpcOps().request_rpc_server_name_rpc(data=payload) end = datetime.datetime.now() diff_seconds = (end - start).total_seconds() LogUtil.info(u"-----------------【rpc Response】 ----------------") LogUtil.info(u" errno: {}, msg: {}, data: {}".format(errno, msg, data.__repr__().decode("unicode-escape"))) LogUtil.info(u"-----------------------------------------------") LogUtil.info(u"【性能】请求花费时间 {} s ".format(diff_seconds)) LogUtil.info(u"-----------------------------------------------") if errno: raise Exception(u"MS 接口调用结果错误,msg:{}".format(msg)) return data
def log_response_details(response_body): LogUtil.info( "-----[RESPONSE] details below : ----------------------------") LogUtil.info('Returned code: {}'.format(response_body.status_code)) if response_body.status_code != 200: LogUtil.info("[WARN]Return code is not 200!!!! but {} .".format( response_body.status_code)) err_msg = response_body.text try: json_body = response_body.json() s = str(json_body).replace('u\'', '\'') LogUtil.info(' Response: ') LogUtil.info(s.decode('unicode-escape')) err_msg = s.decode('unicode-escape') except Exception as e: LogUtil.error('Response raw data: ') LogUtil.exception(err_msg, e) LogUtil.error( u"\n【FATAL ERROR】检查请求细节是否出错,或者服务器是否有问题\n ---->> {}". format(err_msg)) else: try: json_body = response_body.json() s = str(json_body).replace('u\'', '\'') LogUtil.info(' Response: ') LogUtil.info(s.decode('unicode-escape')) except: LogUtil.info(u' Response raw data: \n{}'.format( response_body.text)) raise Exception("http请求的返回不是Json格式,检查服务器是否有问题") LogUtil.info( "------------------------------------------------------------------------" )
def log_request_details(url=None, headers=None, cookies=None, params=None, data=None, json_data=None, method=None): LogUtil.info( "-----[REQUEST] details below : ----------------------------") time_stamp = datetime.datetime.now() LogUtil.info(" Current time : {}".format( time_stamp.strftime('%Y.%m.%d-%H:%M:%S'))) LogUtil.info(" Request : {}".format(url)) if headers: LogUtil.info(" Headers : {}".format(headers)) if cookies: LogUtil.info(" Cookies : {}".format(cookies)) if params: try: LogUtil.info(" Parameters : {}".format( params.__repr__().decode("unicode-escape"))) except: LogUtil.info(" Parameters : {}".format(params)) if method: LogUtil.info(" Method : {}".format(method)) if data: try: LogUtil.info(" Payload : {}".format( data.__repr__().decode("unicode-escape"))) except: LogUtil.info(" Payload : {}".format(data)) if json_data: try: LogUtil.info(" Payload-json : {}".format( json_data.__repr__().decode("unicode-escape"))) except: LogUtil.info(" Payload-json : {}".format(json_data)) request_obj = {} request_obj['url'] = url request_obj['method'] = method request_obj['headers'] = headers request_obj['cookies'] = cookies request_obj['parameters'] = params request_obj['payload'] = data return request_obj
def merge_html(self, project_name, duration, merge_report_file_name, local=False): reports = {} index = 0 head_list = [] import os _re = project_name + '*html*' file_path = os.path.join(ROOT_DIR, "report", _re) for file in glob.glob(file_path): LogUtil.info(file) reports[index] = file index += 1 d = pq(filename=file, parser='html') if not d: continue head_list.append(d('div').filter(".heading")('.attribute').text().split()) start_time = min([head[2] for head in head_list]) + ' ' + min([head[3] for head in head_list]) duration = duration total = sum([int(head[13]) for head in head_list]) _pass = sum([int(head[17]) for head in head_list if head[15] == 'Pass']) # python 2.7 两个整数相除结果为整数,需要将至少一个转换为浮点数 pass_rate = round(float(_pass) / total * 100, 2) failure = sum(int(head[head.index('Failure') + 2]) for head in head_list if 'Failure' in head) error = sum(int(head[head.index('Error') + 2]) for head in head_list if 'Error' in head) message = u""" <html> <head> <body> {0} <div> {1} </div> </body> </head> </html>""" head = u""" <div class='heading'> <h1>{} API Automation Test Report</h1> <p class='attribute'><strong>Start Time:</strong> {}</p> <p class='attribute'><strong>Duration:</strong> {}min {}s</p> <p class='attribute'><strong>Pass Rate:</strong> {} %</p> <p class='attribute'><strong>Statistics:</strong> Total: {}; Pass: {}; Failure: {}; Error: {}; </p> <p class='description'></p> </div> """.format(project_name.upper(), start_time, int(duration)/60, int(duration)%60, pass_rate, total, _pass, failure, error) insert = [] for k, v in reports.items(): if not local: v = v.split('/')[-1] insert.append('<iframe id={} class="contentView" ' 'style="width: 100%;height: 400px;overflow: hidden;border: none;" ' 'src="{}"></iframe>'.format(k,v)) res = message.format(head, ''.join(insert)) # # 生成合并后的文件名 merge_full_path = os.path.join(ROOT_DIR, "report", merge_report_file_name) with open(merge_full_path, "w") as merge_file: merge_file.write(self.encode_utf8(res)) return start_time, pass_rate, total, _pass, failure, error