def run(self, path_or_tests, dot_env_path=None, mapping=None): """ main interface. Args: path_or_tests: str: testcase/testsuite file/foler path dict: valid testcase/testsuite data dot_env_path (str): specified .env file path. mapping (dict): if mapping is specified, it will override variables in config block. Returns: dict: result summary """ logger.log_info("HttpRunner version: {}".format(__version__)) if loader.is_test_path(path_or_tests): return self.run_path(path_or_tests, dot_env_path, mapping) elif loader.is_test_content(path_or_tests): project_working_directory = path_or_tests.get( "project_mapping", {}).get("PWD", os.getcwd()) loader.init_pwd(project_working_directory) return self.run_tests(path_or_tests) else: raise exceptions.ParamsError( "Invalid testcase path or testcases: {}".format(path_or_tests))
def getTokenFromHodor(user, password): try: project_working_directory = os.getcwd() goFileToGetToken = os.path.join(project_working_directory, "aleo-go/src/aleo-e2e/cmd/hodor-auth") newGoEnvPATH = os.path.join(project_working_directory, "aleo-go") whole_cmd = 'go run {} --url {} --user {} --password {}'.format( goFileToGetToken, BASE_URL, user, password) s = subprocess.Popen(whole_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, env={"GOPATH": newGoEnvPATH}) error = s.stderr.read().decode('utf8') if error: logger.log_info( 'cannot get the token, error message is {}'.format(error)) raise Exception() result = s.stdout.read().decode('utf8') token = result.split('\n')[-2] logger.log_info('the token is {}'.format(token)) except Exception as e: logger.log_error("cannot get hodor token") logger.logging.exception(e) return "Bearer " + token
def extract_response(self, extractors): """ extract value from requests.Response and store in OrderedDict. @param (list) extractors [ {"resp_status_code": "status_code"}, {"resp_headers_content_type": "headers.content-type"}, {"resp_content": "content"}, {"resp_content_person_first_name": "content.person.name.first_name"} ] @return (OrderDict) variable binds ordered dict """ if not extractors: return {} logger.log_info("start to extract from response object.") extracted_variables_mapping = OrderedDict() extract_binds_order_dict = utils.convert_to_order_dict(extractors) for key, field in extract_binds_order_dict.items(): if not isinstance(field, basestring): raise exception.ParamsError("invalid extractors in testcase!") extracted_variables_mapping[key] = self.extract_field(field) return extracted_variables_mapping
def __init__(self, failfast=False, save_tests=False, report_template=None, report_dir=None, log_level="INFO", log_file=''): """ initialize HttpRunner. Args: failfast (bool): 在第一个错误或者失败时,停止测试运行. save_tests (bool): 将加载/解析的测试保存到json文件. report_template (str): 报表模板文件路径,模板应为jinja2格式. report_dir (str): html报告保存目录. log_level (str): 日志等级. log_file (str): 日志文件前缀名称. """ log_name_timestamp = datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d') log_path = os.path.join(os.getcwd(), 'logs') if not os.path.exists(log_path): os.mkdir(log_path) # 创建logs目录 log_file_path = os.path.join(log_path, (log_file + log_name_timestamp + ".log")) logger.setup_logger(log_level, log_file_path) # 调用日志函数,参数 日志等级和日志文件路径 logger.log_info("HttpRunner version: {}".format(__version__)) self.exception_stage = "initialize HttpRunner()" kwargs = { "failfast": failfast, "resultclass": report.HtmlTestResult } self.unittest_runner = unittest.TextTestRunner(**kwargs) # 实例化 TextTestRunner self.test_loader = unittest.TestLoader() # 实例化 TestLoader self.save_tests = save_tests self.report_template = report_template self.report_dir = report_dir self._summary = None
def validate(self, validators, resp_obj): ''' make validations ''' evaluated_validators = [] if not validators: return evaluated_validators logger.log_info('start to validate.') validate_pass = True for vaildator in validators: # evaluate validators with context variable mapping. evaluated_validator = self.__eval_check_item( parser.parse_validator(vaildator), resp_obj) try: self._do_validation(evaluated_validator) except exceptions.VaildationFailure: validate_pass = False evaluated_validators.append(evaluated_validator) if not validate_pass: raise exceptions.VaildationFailure
def __init__(self, failfast=False, save_tests=False, report_template=None, report_dir=None, log_level="DEBUG", log_file=None): """ initialize HttpRunner. Args: failfast (bool): stop the test run on the first error or failure. save_tests (bool): save loaded/parsed tests to JSON file. report_template (str): report template file path, template should be in Jinja2 format. report_dir (str): html report save directory. log_level (str): logging level. log_file (str): log file path. """ logger.setup_logger(log_level, log_file) logger.log_info("HttpRunner version: {}".format(__version__)) self.exception_stage = "initialize HttpRunner()" kwargs = {"failfast": failfast, "resultclass": report.HtmlTestResult} self.unittest_runner = unittest.TextTestRunner(**kwargs) self.test_loader = unittest.TestLoader() self.save_tests = save_tests self.report_template = report_template self.report_dir = report_dir self._summary = None
def extract_response(self, extractors): """ extract value from requests.Response and store in OrderedDict. @param (list) extractors [ {"resp_status_code": "status_code"}, {"resp_headers_content_type": "headers.content-type"}, {"resp_content": "content"}, {"resp_content_person_first_name": "content.person.name.first_name"} ] @return (OrderDict) variable binds ordered dict """ if not extractors: return {} logger.log_info("start to extract from response object.") extracted_variables_mapping = OrderedDict() extract_binds_order_dict = utils.convert_to_order_dict(extractors) for key, field in extract_binds_order_dict.items(): if not isinstance(field, basestring): raise exception.ParamsError("invalid extractors in testcase!") extracted_variables_mapping[key] = self.extract_field(field) return extracted_variables_mapping
def dbvalidate(self, dbvalidators): """ validate datebase item """ evaluated_dbvalidators = [] if not dbvalidators: return evaluated_dbvalidators logger.log_info("start to dbvalidate.") dbvalidate_pass = True failures = [] for dbvalidator in dbvalidators: # evaluate dbvalidators with context variable mapping. evaluated_dbvalidator = self.eval_content( parser.parse_dbvalidator(dbvalidator)) try: self._do_dbvalidation(evaluated_dbvalidator) except exceptions.DbValidationFailure as ex: dbvalidate_pass = False failures.append(str(ex)) evaluated_dbvalidators.append(evaluated_dbvalidator) if not dbvalidate_pass: failures_string = "\n".join([failure for failure in failures]) raise exceptions.DbValidationFailure(failures_string)
def extract_response(self, extractors): """ extract value from requests.Response and store in OrderedDict. @param (list) extractors [ {"resp_status_code": "status_code"}, {"resp_headers_content_type": "headers.content-type"}, {"resp_content": "content"}, {"resp_content_person_first_name": "content.person.name.first_name"} ] @return (OrderDict) variable binds ordered dict """ if not extractors: return {} logger.log_info("start to extract from response object.") extracted_variables_mapping = OrderedDict() extract_binds_order_dict = utils.convert_to_order_dict(extractors) for key, field in extract_binds_order_dict.items(): if not isinstance(field, basestring): raise exception.ParamsError("invalid extractors in testcase!") result = self.extract_field(field) extracted_variables_mapping[key] = result if not (isinstance(result, bool) or bool(result)): # False 可以return err_msg = u"extract data with delimiter can be None!\n" err_msg += u"response: {}\n".format(self.parsed_dict()) err_msg += u"regex: {}\n".format(field) logger.log_error(err_msg) raise exception.ParamsError(err_msg) return extracted_variables_mapping
def validate(self, validators, resp_obj): """ make validations """ if not validators: return logger.log_info("start to validate.") self.evaluated_validators = [] validate_pass = True for validator in validators: # evaluate validators with context variable mapping. evaluated_validator = self.eval_check_item( testcase.parse_validator(validator), resp_obj ) try: self.do_validation(evaluated_validator) except exceptions.ValidationFailure: validate_pass = False self.evaluated_validators.append(evaluated_validator) if not validate_pass: raise exceptions.ValidationFailure
def load_dot_env_file(path): """ load .env file """ if not path: path = os.path.join(os.getcwd(), ".env") if not os.path.isfile(path): logger.log_debug(".env file not exist: {}".format(path)) return {} else: if not os.path.isfile(path): raise exceptions.FileNotFound( "env file not exist: {}".format(path)) logger.log_info("Loading environment variables from {}".format(path)) env_variables_mapping = {} with io.open(path, 'r', encoding='utf-8') as fp: for line in fp: if "=" in line: variable, value = line.split("=") elif ":" in line: variable, value = line.split(":") else: raise exceptions.FileFormatError(".env format error") env_variables_mapping[variable.strip()] = value.strip() return env_variables_mapping
def validate(self, validators, resp_obj): """ make validations """ evaluated_validators = [] if not validators: return evaluated_validators logger.log_info("start to validate.") validate_pass = True failures = [] for validator in validators: # evaluate validators with context variable mapping. evaluated_validator = self.__eval_check_item( parser.parse_validator(validator), resp_obj) try: self._do_validation(evaluated_validator) except exceptions.ValidationFailure as ex: validate_pass = False failures.append(str(ex)) evaluated_validators.append(evaluated_validator) if not validate_pass: failures_string = "\n".join([failure for failure in failures]) raise exceptions.ValidationFailure(failures_string) return evaluated_validators
def render_html_report(summary, html_report_name=None, html_report_template=None, data_or_report=False): """ 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__)), Path("templates"), Path("default_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.path.abspath('.') + r'/reports') 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" else: summary["html_report_name"] = "" 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", ) as fp_r: template_content = fp_r.read() # rendered_content = Template(template_content).render(summary) report_path = os.path.join(report_dir_path, html_report_name) # report_path = r'E:\project\source/reports\aaa.html' rendered_content = Template(template_content, extensions=["jinja2.ext.loopcontrols" ]).render(summary) if data_or_report: return rendered_content with io.open( report_path, 'w', ) as fp_w: fp_w.write(rendered_content) return report_path
def request(self, method, url, name=None, **kwargs): self.init_meta_data() # record test name self.meta_data["name"] = name # record original request info self.meta_data["data"][0]["request"]["method"] = method self.meta_data["data"][0]["request"]["url"] = url kwargs.setdefault("timeout", 120) self.meta_data["data"][0]["request"].update(kwargs) # prepend url with hostname unless it's already an absolute URL url = build_url(self.base_url, url) start_timestamp = time.time() response = self._send_request_safe_mode(method, url, **kwargs) # requests包get响应内容中文乱码解决 if response.apparent_encoding: response.encoding = response.apparent_encoding response_time_ms = round((time.time() - start_timestamp) * 1000, 2) # get the length of the content, but if the argument stream is set to True, we take # the size from the content-length header, in order to not trigger fetching of the body if kwargs.get("stream", False): content_size = int(dict(response.headers).get("content-length") or 0) else: content_size = len(response.content or "") # record the consumed time self.meta_data["stat"] = { "response_time_ms": response_time_ms, "elapsed_ms": response.elapsed.microseconds / 1000.0, "content_size": content_size } # record request and response histories, include 30X redirection response_list = response.history + [response] self.meta_data["data"] = [ self.get_req_resp_record(resp_obj) for resp_obj in response_list ] self.meta_data["data"][0]["request"].update(kwargs) try: response.raise_for_status() except RequestException as e: logger.log_error(u"{exception}".format(exception=str(e))) else: logger.log_info( """status_code: {}, response_time(ms): {} ms, response_length: {} bytes\n""" .format(response.status_code, response_time_ms, content_size)) return response
def load_dot_env_file(path): """ load .env file and set to os.environ """ if not os.path.isfile(path): return logger.log_info("Loading environment variables from {}".format(path)) with io.open(path, 'r', encoding='utf-8') as fp: for line in fp: variable, value = line.split("=") os.environ[variable] = value logger.log_debug("Loaded variable: {}".format(variable))
def main_hrun(testset_path, executor=None, request_data=None): """ 用例运行 :param testset_path: dict or list :param report_name: str :return: """ logger.setup_logger('INFO') kwargs = { "failfast": False, } runner = HttpRunner(**kwargs) error_info = { 'start_time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') } report_type = 'test' report_name = '执行异常' try: id = request_data.pop('id') base_url = request_data.pop('env_name') type = request_data.pop('type') report_name = request_data.get('report_name', None) if type: report_type = type report_name2 = run_test_by_type(id, base_url, testset_path, type) if not report_name: report_name = report_name2 runner.run2(testset_path) shutil.rmtree(testset_path) runner.summary = timestamp_to_datetime(runner.summary) report_path = add_test_reports(runner, report_name=report_name, report_type=report_type, executor=executor) except Exception as e: logger.log_info("出现异常: {0}".format(e)) error_info['error_msg'] = "出现异常: {0}".format(e) add_error_reports(error_info, report_name=report_name, report_type=report_type, executor=executor) except BaseException as e: logger.log_info("出现异常: {0}".format(e)) error_info['error_msg'] = "出现异常: {0}".format(e) add_error_reports(error_info, report_name=report_name, report_type=report_type, executor=executor)
def print_info(info_mapping): """ print info in mapping. Args: info_mapping (dict): input(variables) or output mapping. Examples: >>> info_mapping = { "var_a": "hello", "var_b": "world" } >>> info_mapping = { "status_code": 500 } >>> print_info(info_mapping) ==================== Output ==================== Key : Value ---------------- : ---------------------------- var_a : hello var_b : world ------------------------------------------------ """ if not info_mapping: return content_format = "{:<16} : {:<}\n" content = "\n==================== Output ====================\n" content += content_format.format("Variable", "Value") content += content_format.format("-" * 16, "-" * 29) for key, value in info_mapping.items(): if isinstance(value, (tuple, collections.deque)): continue elif isinstance(value, (dict, list)): value = json.dumps(value) elif value is None: value = "None" if is_py2: if isinstance(key, unicode): key = key.encode("utf-8") if isinstance(value, unicode): value = value.encode("utf-8") content += content_format.format(key, value) content += "-" * 48 + "\n" logger.log_info(content)
def run(self, path_or_tests, dot_env_path=None, mapping=None): """ main interface. Args: path_or_tests: str: testcase/testsuite file/foler path dict: valid testcase/testsuite data """ logger.log_info("HttpRunner version: {}".format(__version__)) if validator.is_testcase_path(path_or_tests): return self.run_path(path_or_tests, dot_env_path, mapping) elif validator.is_testcases(path_or_tests): return self.run_tests(path_or_tests) else: raise exceptions.ParamsError( "Invalid testcase path or testcases: {}".format(path_or_tests))
def load_dot_env_file(path): """ load .env file Args: path (str): .env file path. If path is None, it will find .env file in current working directory. Returns: dict: environment variables mapping { "UserName": "******", "Password": "******", "PROJECT_KEY": "ABCDEFGH" } Raises: exceptions.FileNotFound: If specified env file is not exist. exceptions.FileFormatError: If env file format is invalid. """ if not path: path = os.path.join(os.getcwd(), ".env") if not os.path.isfile(path): logger.log_debug(".env file not exist: {}".format(path)) return {} else: if not os.path.isfile(path): raise exceptions.FileNotFound( "env file not exist: {}".format(path)) logger.log_info("Loading environment variables from {}".format(path)) env_variables_mapping = {} with io.open(path, 'r', encoding='utf-8') as fp: for line in fp: if "=" in line: variable, value = line.split("=") elif ":" in line: variable, value = line.split(":") else: raise exceptions.FileFormatError(".env format error") env_variables_mapping[variable.strip()] = value.strip() project_mapping["env"] = env_variables_mapping return env_variables_mapping
def write_data(res, json_path): """ 把处理后的参数写入json文件 :param res: :param json_path: :return: """ if isinstance(res, dict) or isinstance(res, list): with open(json_path, 'w', encoding='utf-8') as f: json.dump(res, f, ensure_ascii=False, sort_keys=True, indent=4) logger.log_info( 'Interface Params Total:{} ,write to json file successfully! {}\n'.format(len(res), json_path)) elif isinstance(res, str): with open(json_path, "w", encoding='utf-8') as f: f.write(res) else: logger.log_error('{} Params is not dict.\n'.format(write_data.__name__))
def load_dot_env_file(): """ load .env file, .env file should be located in project working directory by default. If dot_env_path is specified, it will be loaded instead. Returns: dict: environment variables mapping { "UserName": "******", "Password": "******", "PROJECT_KEY": "ABCDEFGH" } Raises: exceptions.FileFormatError: If env file format is invalid. """ path = dot_env_path or os.path.join(project_working_directory, ".env") if not os.path.isfile(path): if dot_env_path: logger.log_error(".env file not exist: {}".format(dot_env_path)) sys.exit(1) else: logger.log_debug( ".env file not exist in: {}".format(project_working_directory)) return {} logger.log_info("Loading environment variables from {}".format(path)) env_variables_mapping = {} with io.open(path, 'r', encoding='utf-8') as fp: for line in fp: # maxsplit=1 if "=" in line: variable, value = line.split("=", 1) elif ":" in line: variable, value = line.split(":", 1) else: raise exceptions.FileFormatError(".env format error") env_variables_mapping[variable.strip()] = value.strip() project_mapping["env"] = env_variables_mapping utils.set_os_environ(env_variables_mapping) return env_variables_mapping
def _run_suite(self, test_suite): """ run tests in test_suite Args: test_suite: unittest.TestSuite() Returns: list: tests_results """ tests_results = [] for testcase in test_suite: testcase_name = testcase.config.get("name") logger.log_info("Start to run testcase: {}".format(testcase_name)) result = self.unittest_runner.run(testcase) tests_results.append((testcase, result)) return tests_results
def load_dot_env_file(path): """ load .env file and set to os.environ """ if not path: path = os.path.join(os.getcwd(), ".env") if not os.path.isfile(path): logger.log_debug(".env file not exist: {}".format(path)) return else: if not os.path.isfile(path): raise exception.FileNotFoundError("env file not exist: {}".format(path)) logger.log_info("Loading environment variables from {}".format(path)) with io.open(path, 'r', encoding='utf-8') as fp: for line in fp: variable, value = line.split("=") variable = variable.strip() os.environ[variable] = value.strip() logger.log_debug("Loaded variable: {}".format(variable))
def load_dot_env_file(path): """ load .env file and set to os.environ """ if not path: path = os.path.join(os.getcwd(), ".env") if not os.path.isfile(path): logger.log_debug(".env file not exist: {}".format(path)) return else: if not os.path.isfile(path): raise exceptions.FileNotFound("env file not exist: {}".format(path)) logger.log_info("Loading environment variables from {}".format(path)) with io.open(path, 'r', encoding='utf-8') as fp: for line in fp: variable, value = line.split("=") variable = variable.strip() os.environ[variable] = value.strip() logger.log_debug("Loaded variable: {}".format(variable))
def load_dot_env_file(dot_env_path): """ load .env file. Args: dot_env_path (str): .env file path Returns: dict: environment variables mapping { "UserName": "******", "Password": "******", "PROJECT_KEY": "ABCDEFGH" } Raises: exceptions.FileFormatError: If .env file format is invalid. """ if not os.path.isfile(dot_env_path): return {} logger.log_info( "Loading environment variables from {}".format(dot_env_path)) env_variables_mapping = {} with io.open(dot_env_path, 'r', encoding='utf-8') as fp: for line in fp: # maxsplit=1 if "=" in line: variable, value = line.split("=", 1) elif ":" in line: variable, value = line.split(":", 1) elif line.startswith("#") or line.startswith("\n"): pass else: raise exceptions.FileFormatError(".env format error") env_variables_mapping[variable.strip()] = value.strip() utils.set_os_environ(env_variables_mapping) return env_variables_mapping
def _send_request_safe_mode(self, host, port, service, method, params, **kwargs): """ Send a dubbo request TODO: 设计可选参数,设计ApiResponse使用例出现问题后处理不出错 """ meta_data = { "host": host, "port": port, "service": service, "method": method, "params": params, } try: msg = "processed request:\n" msg += "> {service}.{method}\n".format(service=service, method=method) msg += "> params: {params}".format(params=params) self._dubbo.set_host(host, port) response_data = self._dubbo.invoke(service, method, params) response = { "body": response_data, "request": { "host": host, "port": port, "service": service, "method": method } } except timeout as e: logger.log_error("{exception}".format(exception=str(e))) response = DubboApiResponse(**meta_data) response.error = e except Exception as e: logger.log_info( "something unknown happened while requesting {}.{}".format( service, method)) response = DubboApiResponse(**meta_data) response.error = e return response
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", "default_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.path.abspath('..') + r'/reports') start_datetime = summary["time"]["start_at"] 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_datetime) else: summary["html_report_name"] = "" if not os.path.isdir(report_dir_path): os.makedirs(report_dir_path) for record in summary.get("records"): meta_data = record['meta_data'] stringify_body(meta_data, 'request') stringify_body(meta_data, 'response') with io.open(html_report_template, "r", encoding='utf-8') as fp_r: template_content = fp_r.read() rendered_content = Template(template_content).render(summary) return rendered_content
def run_tests(self, unittest_runner, test_suite): """ run tests with unittest_runner and test_suite Args: unittest_runner: unittest.TextTestRunner() test_suite: unittest.TestSuite() Returns: list: tests_results """ self.exception_stage = "running tests" tests_results = [] for testcase in test_suite: testcase_name = testcase.config.get("name") logger.log_info("Start to run testcase: {}".format(testcase_name)) result = unittest_runner.run(testcase) tests_results.append((testcase, result)) return tests_results
def validate(self, validators, resp_obj): """ make validations """ evaluated_validators = [] if not validators: return evaluated_validators logger.log_info("start to validate.") failures = [] for validator in validators: # evaluate validators with context variable mapping. evaluated_validator = self.__eval_check_item( parser.parse_validator(validator), resp_obj) try: self._do_validation(evaluated_validator) except exceptions.ValidationFailure as ex: failures.append(str(ex)) evaluated_validators.append(evaluated_validator) return evaluated_validators
def _run_suite(self, test_suite): """ 运行测试套件 Args: test_suite: unittest.TestSuite() Returns: list: tests_results测试结果 """ tests_results = [] for testcase in test_suite: # 遍历用例集中的每一个用例 testcase_name = testcase.config.get("name") logger.log_info("Start to run testcase: {}".format(testcase_name)) result = self.unittest_runner.run(testcase) if result.wasSuccessful(): tests_results.append((testcase, result)) else: tests_results.insert(0, (testcase, result)) return tests_results
def load_env_file(): ''' load .env file, .env file should be located in project working directory. Returns: dict: enviroment variables mapping { 'username':'******', 'password':'******', 'PROJECT_KEY':'ABCDEFGH' } Raises: exceptions.FileFormatError: If env file format is invalid. ''' path = os.path.join(project_working_directory, '.env') if not os.path.isfile(path): logger.log_debug( f'.env file not exist in: {project_working_directory}') return {} logger.log_info(f'Loading enviroment variables from {path}') env_variables_mapping = {} with open(path, 'r', encoding='utf-8') as fp: for line in fp: if '=' in line: variable, value = line.split('=') elif ':' in line: variable, value = line.split(':') else: raise exceptions.FileFormatError('.env format error') env_variables_mapping[variable.strip()] = value.strip() project_mapping['env'] = env_variables_mapping utils.set_os_environ(env_variables_mapping) return env_variables_mapping
def gen_html_report(summary, report_template=None, report_dir=None, report_file=None): """ render html report with specified report name and template Args: summary (dict): test result summary data report_template (str): specify html report template path, template should be in Jinja2 format. report_dir (str): specify html report save directory report_file (str): specify html report file path, this has higher priority than specifying report dir. """ if not summary["time"] or summary["stat"]["testcases"]["total"] == 0: logger.log_error("test result summary is empty ! {}".format(summary)) raise SummaryEmpty if not report_template: report_template = os.path.join( os.path.abspath(os.path.dirname(__file__)), "template.html") logger.log_debug("No html report template specified, use default.") else: logger.log_info( "render with html report template: {}".format(report_template)) logger.log_info("Start to render Html report ...") start_at_timestamp = summary["time"]["start_at"] utc_time_iso_8601_str = datetime.utcfromtimestamp( start_at_timestamp).isoformat() summary["time"]["start_datetime"] = utc_time_iso_8601_str if report_file: report_dir = os.path.dirname(report_file) report_file_name = os.path.basename(report_file) else: report_dir = report_dir or os.path.join(os.getcwd(), "reports") # fix #826: Windows does not support file name include ":" report_file_name = "{}.html".format( utc_time_iso_8601_str.replace(":", "").replace("-", "")) if not os.path.isdir(report_dir): os.makedirs(report_dir) report_path = os.path.join(report_dir, report_file_name) # custom: add filter, by zheng.zhang template_dir, template_file = os.path.split(report_template) env = Environment(loader=FileSystemLoader(template_dir), extensions=["jinja2.ext.loopcontrols"]) template = env.get_template(template_file) with io.open(report_path, 'w', encoding='utf-8') as fp_w: rendered_content = template.render(summary) fp_w.write(rendered_content) logger.log_info("Generated Html report: {}".format(report_path)) return report_path
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", "default_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_datetime = summary["time"]["start_at"].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_datetime) else: summary["html_report_name"] = "" html_report_name = "{}.html".format(start_datetime) if not os.path.isdir(report_dir_path): os.makedirs(report_dir_path) for record in summary.get("records"): meta_data = record['meta_data'] stringify_body(meta_data, 'request') stringify_body(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).render(summary) fp_w.write(rendered_content) logger.log_info("Generated Html report: {}".format(report_path)) return report_path
def request(self, method, url, name=None, **kwargs): """ Constructs and sends a :py:class:`requests.Request`. Returns :py:class:`requests.Response` object. :param method: method for the new :class:`Request` object. :param url: URL for the new :class:`Request` object. :param name: (optional) Placeholder, make compatible with Locust's HttpSession :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`. :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. :param files: (optional) Dictionary of ``'filename': file-like-objects`` for multipart encoding upload. :param auth: (optional) Auth tuple or callable to enable Basic/Digest/Custom HTTP Auth. :param timeout: (optional) How long to wait for the server to send data before giving up, as a float, or \ a (`connect timeout, read timeout <user/advanced.html#timeouts>`_) tuple. :type timeout: float or tuple :param allow_redirects: (optional) Set to True by default. :type allow_redirects: bool :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. :param stream: (optional) whether to immediately download the response content. Defaults to ``False``. :param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided. :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. """ # store detail data of request and response self.meta_data = {} # prepend url with hostname unless it's already an absolute URL url = self._build_url(url) # set up pre_request hook for attaching meta data to the request object self.meta_data["method"] = method kwargs.setdefault("timeout", 120) self.meta_data["request_time"] = time.time() response = self._send_request_safe_mode(method, url, **kwargs) # record the consumed time self.meta_data["response_time_ms"] = round((time.time() - self.meta_data["request_time"]) * 1000, 2) self.meta_data["elapsed_ms"] = response.elapsed.microseconds / 1000.0 self.meta_data["url"] = (response.history and response.history[0] or response)\ .request.url self.meta_data["request_headers"] = response.request.headers self.meta_data["request_body"] = response.request.body self.meta_data["status_code"] = response.status_code self.meta_data["response_headers"] = response.headers try: self.meta_data["response_body"] = response.json() except ValueError: self.meta_data["response_body"] = response.content msg = "response details:\n" msg += "> status_code: {}\n".format(self.meta_data["status_code"]) msg += "> headers: {}\n".format(self.meta_data["response_headers"]) msg += "> body: {}".format(self.meta_data["response_body"]) logger.log_debug(msg) # get the length of the content, but if the argument stream is set to True, we take # the size from the content-length header, in order to not trigger fetching of the body if kwargs.get("stream", False): self.meta_data["content_size"] = int(self.meta_data["response_headers"].get("content-length") or 0) else: self.meta_data["content_size"] = len(response.content or "") try: response.raise_for_status() except RequestException as e: logger.log_error(u"{exception}".format(exception=str(e))) else: logger.log_info( """status_code: {}, response_time(ms): {} ms, response_length: {} bytes""".format( self.meta_data["status_code"], self.meta_data["response_time_ms"], self.meta_data["content_size"] ) ) return response
def run_test(self, testcase_dict): """ run single testcase. @param (dict) testcase_dict { "name": "testcase description", "skip": "skip this test unconditionally", "times": 3, "requires": [], # optional, override "function_binds": {}, # optional, override "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 } @return True or raise exception during test """ # check skip self._handle_skip_feature(testcase_dict) # prepare parsed_request = self.init_config(testcase_dict, level="testcase") self.context.bind_testcase_variable("request", parsed_request) # setup hooks setup_hooks = testcase_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 exception.ParamsError("URL or METHOD missed!") 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 = testcase_dict.get("teardown_hooks", []) if teardown_hooks: self.context.bind_testcase_variable("response", resp_obj) self.do_hook_actions(teardown_hooks) # extract extractors = testcase_dict.get("extract", []) or testcase_dict.get("extractors", []) extracted_variables_mapping = resp_obj.extract_response(extractors) self.context.bind_extracted_variables(extracted_variables_mapping) # validate validators = testcase_dict.get("validate", []) or testcase_dict.get("validators", []) try: self.context.validate(validators, resp_obj) except (exception.ParamsError, exception.ResponseError, \ exception.ValidationError, exception.ParseResponseError): # 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, 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 += "content: {}\n".format(resp_obj.content) logger.log_error(err_resp_msg) raise