def print_io(in_out): """ print input(variables) and output. Args: in_out (dict): input(variables) and output mapping. Examples: >>> in_out = { "in": { "var_a": "hello", "var_b": "world" }, "out": { "status_code": 500 } } >>> print_io(in_out) ================== Variables & Output ================== Type | Variable : Value ------ | ---------------- : --------------------------- Var | var_a : hello Var | var_b : world Out | status_code : 500 -------------------------------------------------------- """ content_format = "{:<6} | {:<16} : {:<}\n" content = "\n================== Variables & Output ==================\n" content += content_format.format("Type", "Variable", "Value") content += content_format.format("-" * 6, "-" * 16, "-" * 27) def prepare_content(var_type, in_out): content = "" for variable, value in in_out.items(): if isinstance(value, tuple): continue elif isinstance(value, (dict, list)): value = json.dumps(value) if is_py2: if isinstance(variable, unicode): variable = variable.encode("utf-8") if isinstance(value, unicode): value = value.encode("utf-8") content += content_format.format(var_type, variable, value) return content _in = in_out["in"] _out = in_out["out"] content += prepare_content("Var", _in) content += "\n" content += prepare_content("Out", _out) content += "-" * 56 + "\n" logger.log_debug(content)
def render_html_report(summary, html_report_name=None, html_report_template=None): """ render html report with specified report name and template if html_report_name is not specified, use current datetime if html_report_template is not specified, use default report template """ if not html_report_template: html_report_template = os.path.join( os.path.abspath(os.path.dirname(__file__)), "templates", "report_template.html") logger.log_debug("No html report template specified, use default.") else: logger.log_info("render with html report template: {}".format( html_report_template)) logger.log_info("Start to render Html report ...") logger.log_debug("render data: {}".format(summary)) report_dir_path = os.path.join(os.getcwd(), "reports") start_at_timestamp = int(summary["time"]["start_at"]) summary["time"]["start_datetime"] = datetime.fromtimestamp( start_at_timestamp).strftime('%Y-%m-%d %H:%M:%S') if html_report_name: summary["html_report_name"] = html_report_name report_dir_path = os.path.join(report_dir_path, html_report_name) html_report_name += "-{}.html".format(start_at_timestamp) else: summary["html_report_name"] = "" html_report_name = "{}.html".format(start_at_timestamp) if not os.path.isdir(report_dir_path): os.makedirs(report_dir_path) for index, suite_summary in enumerate(summary["details"]): if not suite_summary.get("name"): suite_summary["name"] = "test suite {}".format(index) for record in suite_summary.get("records"): meta_data = record['meta_data'] stringify_data(meta_data, 'request') stringify_data(meta_data, 'response') with io.open(html_report_template, "r", encoding='utf-8') as fp_r: template_content = fp_r.read() report_path = os.path.join(report_dir_path, html_report_name) with io.open(report_path, 'w', encoding='utf-8') as fp_w: rendered_content = Template(template_content, extensions=["jinja2.ext.loopcontrols" ]).render(summary) fp_w.write(rendered_content) logger.log_info("Generated Html report: {}".format(report_path)) return report_path
def _do_validation(self, validator_dict): """ validate with functions Args: validator_dict (dict): validator dict { "check": "status_code", "check_value": 200, "expect": 201, "comparator": "eq" } """ # TODO: move comparator uniform to init_test_suites comparator = utils.get_uniform_comparator(validator_dict["comparator"]) validate_func = parser.get_mapping_function( comparator, self.TESTCASE_SHARED_FUNCTIONS_MAPPING) check_item = validator_dict["check"] check_value = validator_dict["check_value"] expect_value = validator_dict["expect"] if (check_value is None or expect_value is None) \ and comparator not in ["is", "eq", "equals", "=="]: raise exceptions.ParamsError( "Null value can only be compared with comparator: eq/equals/==" ) validate_msg = "validate: {} {} {}({})".format( check_item, comparator, expect_value, type(expect_value).__name__) try: validator_dict["check_result"] = "pass" validate_func(check_value, expect_value) validate_msg += "\t==> pass" logger.log_debug(validate_msg) except (AssertionError, TypeError): validate_msg += "\t==> fail" validate_msg += "\n{}({}) {} {}({})".format( check_value, type(check_value).__name__, comparator, expect_value, type(expect_value).__name__) logger.log_error(validate_msg) validator_dict["check_result"] = "fail" raise exceptions.ValidationFailure(validate_msg)
def _send_request_safe_mode(self, method, url, **kwargs): """ Send a HTTP request, and catch any exception that might occur due to connection problems. Safe mode has been removed from requests 1.x. """ try: msg = "processed request:\n" msg += "> {method} {url}\n".format(method=method, url=url) msg += "> kwargs: {kwargs}".format(kwargs=kwargs) logger.log_debug(msg) return requests.Session.request(self, method, url, **kwargs) except (MissingSchema, InvalidSchema, InvalidURL): raise except RequestException as ex: resp = ApiResponse() resp.error = ex resp.status_code = 0 # with this status_code, content returns None resp.request = Request(method, url).prepare() return resp
def extract_field(self, field): """ extract value from requests.Response. """ if not isinstance(field, basestring): err_msg = u"Invalid extractor! => {}\n".format(field) logger.log_error(err_msg) raise exceptions.ParamsError(err_msg) msg = "extract: {}".format(field) if text_extractor_regexp_compile.match(field): value = self._extract_field_with_regex(field) else: value = self._extract_field_with_delimiter(field) if is_py2 and isinstance(value, unicode): value = value.encode("utf-8") msg += "\t=> {}".format(value) logger.log_debug(msg) return value
def log_print(request_response): msg = "\n================== {} details ==================\n".format( request_response) for key, value in self.meta_data[request_response].items(): msg += "{:<16} : {}\n".format(key, repr(value)) logger.log_debug(msg)
def run_test(self, teststep_dict): """ run single teststep. Args: teststep_dict (dict): teststep info { "name": "teststep description", "skip": "skip this test unconditionally", "times": 3, "variables": [], # optional, override "request": { "url": "http://127.0.0.1:5000/api/users/1000", "method": "POST", "headers": { "Content-Type": "application/json", "authorization": "$authorization", "random": "$random" }, "body": '{"name": "user", "password": "******"}' }, "extract": [], # optional "validate": [], # optional "setup_hooks": [], # optional "teardown_hooks": [] # optional } Raises: exceptions.ParamsError exceptions.ValidationFailure exceptions.ExtractFailure """ # check skip self._handle_skip_feature(teststep_dict) # prepare extractors = teststep_dict.get("extract", []) or teststep_dict.get( "extractors", []) validators = teststep_dict.get("validate", []) or teststep_dict.get( "validators", []) parsed_request = self.init_test(teststep_dict, level="teststep") self.context.update_teststep_variables_mapping("request", parsed_request) # setup hooks setup_hooks = teststep_dict.get("setup_hooks", []) setup_hooks.insert(0, "${setup_hook_prepare_kwargs($request)}") self.do_hook_actions(setup_hooks) try: url = parsed_request.pop('url') method = parsed_request.pop('method') group_name = parsed_request.pop("group", None) except KeyError: raise exceptions.ParamsError("URL or METHOD missed!") # TODO: move method validation to json schema valid_methods = [ "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS" ] if method.upper() not in valid_methods: err_msg = u"Invalid HTTP method! => {}\n".format(method) err_msg += "Available HTTP methods: {}".format( "/".join(valid_methods)) logger.log_error(err_msg) raise exceptions.ParamsError(err_msg) logger.log_info("{method} {url}".format(method=method, url=url)) logger.log_debug( "request kwargs(raw): {kwargs}".format(kwargs=parsed_request)) # request resp = self.http_client_session.request(method, url, name=group_name, **parsed_request) resp_obj = response.ResponseObject(resp) # teardown hooks teardown_hooks = teststep_dict.get("teardown_hooks", []) if teardown_hooks: logger.log_info("start to run teardown hooks") self.context.update_teststep_variables_mapping( "response", resp_obj) self.do_hook_actions(teardown_hooks) # extract extracted_variables_mapping = resp_obj.extract_response(extractors) self.context.update_testcase_runtime_variables_mapping( extracted_variables_mapping) # validate try: self.evaluated_validators = self.context.validate( validators, resp_obj) except (exceptions.ParamsError, exceptions.ValidationFailure, exceptions.ExtractFailure): # log request err_req_msg = "request: \n" err_req_msg += "headers: {}\n".format( parsed_request.pop("headers", {})) for k, v in parsed_request.items(): err_req_msg += "{}: {}\n".format(k, repr(v)) logger.log_error(err_req_msg) # log response err_resp_msg = "response: \n" err_resp_msg += "status_code: {}\n".format(resp_obj.status_code) err_resp_msg += "headers: {}\n".format(resp_obj.headers) err_resp_msg += "body: {}\n".format(repr(resp_obj.text)) logger.log_error(err_resp_msg) raise
def do_hook_actions(self, actions): for action in actions: logger.log_debug("call hook: {}".format(action)) # TODO: check hook function if valid self.context.eval_content(action)
def set_os_environ(variables_mapping): """ set variables mapping to os.environ """ for variable in variables_mapping: os.environ[variable] = variables_mapping[variable] logger.log_debug("Loaded variable: {}".format(variable))
def render_html_report(self, **kwargs): """ render html report with specified report name and template if html_report_name is not specified, use current datetime if html_report_template is not specified, use default report template """ # 获取所需参数 ------------------------------------------------------------------------------------------------- summary = self.summary result_stastic= { "testsRun": summary["stat"]["testsRun"], "successes": summary["stat"]["successes"], "errors": summary["stat"]["errors"], "failures": summary["stat"]["failures"], "skipped": summary["stat"]["skipped"]} report_name = kwargs.get("report_name", None) file_name = kwargs.get("file_name", None) dir_name = kwargs.get("dir_name", None) html_report_template = kwargs.get("html_report_template", None) # 选择模板------------------------------------------------------------------------------------------------------ if not html_report_template: html_report_template = os.path.join( os.path.abspath(os.path.dirname(__file__)), "templates", "report_template.html" ) logger.log_debug("No html report template specified, use default.") else: logger.log_info("render with html report template: {}".format(html_report_template)) logger.log_info("Start to render Html report ...") logger.log_debug("render data: {}".format(summary)) # 在reports文件夹下建立dir_name文件夹,如果没有就创建 ---------------------------------------------------------- report_dir_path = os.path.join(os.getcwd(), "reports") report_dir_path = os.path.join(report_dir_path, dir_name) if not os.path.isdir(report_dir_path): os.makedirs(report_dir_path) # 在dir_name文件夹下建立file_name报告 ------------------------------------------------------------------------- start_at_timestamp = int(summary["time"]["start_at"]) summary["time"]["start_datetime"] = datetime.fromtimestamp(start_at_timestamp).strftime('%Y-%m-%d %H_%M_%S') start_time = summary["time"]["start_datetime"] if file_name: summary["html_report_name"] = report_name file_name += "-{}.html".format(start_time) else: summary["html_report_name"] = "" file_name = "{}.html".format(start_time) # 准备报告的Details部分数据 ------------------------------------------------------------------------------------ for index, suite_summary in enumerate(summary["details"]): if not suite_summary.get("name"): suite_summary["name"] = "test suite {}".format(index) for record in suite_summary.get("records"): meta_data = record['meta_data'] report.stringify_data(meta_data, 'request') report.stringify_data(meta_data, 'response') # 模板渲染 ----------------------------------------------------------------------------------------------------- with open(html_report_template, "r", encoding='utf-8') as fp_r: template_content = fp_r.read() # report_path = os.path.join(report_dir_path, file_name) # 报告完整路径 rendered_content = Template(template_content).render(summary) # with open(report_path, 'w', encoding='utf-8') as fp_w: # rendered_content = Template(template_content).render(summary) # 模板渲染 # fp_w.write(rendered_content) # logger.log_info("Generated Html report: {}".format(report_path)) return rendered_content, result_stastic