class TestHttpClient(ApiServerUnittest): def setUp(self): super(TestHttpClient, self).setUp() self.api_client = HttpSession(self.host) self.headers = self.get_authenticated_headers() self.reset_all() def tearDown(self): super(TestHttpClient, self).tearDown() def reset_all(self): url = "%s/api/reset-all" % self.host headers = self.get_authenticated_headers() return self.api_client.get(url, headers=headers) def test_request_with_full_url(self): url = "%s/api/users/1000" % self.host data = {'name': 'user1', 'password': '******'} resp = self.api_client.post(url, json=data, headers=self.headers) self.assertEqual(201, resp.status_code) self.assertEqual(True, resp.json()['success']) def test_request_without_base_url(self): url = "/api/users/1000" data = {'name': 'user1', 'password': '******'} resp = self.api_client.post(url, json=data, headers=self.headers) self.assertEqual(201, resp.status_code) self.assertEqual(True, resp.json()['success']) def test_prepare_kwargs_content_type_application_json_without_charset( self): kwargs = { "headers": { "content-type": "application/json" }, "data": { "a": 1, "b": 2 } } setup_hook_prepare_kwargs("POST", "/path", kwargs) self.assertIsInstance(kwargs["data"], bytes) self.assertIn(b'"a": 1', kwargs["data"]) self.assertIn(b'"b": 2', kwargs["data"]) def test_prepare_kwargs_content_type_application_json_charset_utf8(self): kwargs = { "headers": { "content-type": "application/json; charset=utf-8" }, "data": { "a": 1, "b": 2 } } setup_hook_prepare_kwargs("POST", "/path", kwargs) self.assertIsInstance(kwargs["data"], bytes)
def run(self, testcase: TestCase): """run testcase""" self.config = testcase.config self.teststeps = testcase.teststeps # prepare self.__project_meta = self.__project_meta or load_project_meta( self.config.path) self.__parse_config(self.config) self.__start_at = time.time() self.__step_datas: List[StepData] = [] self.__session = self.__session or HttpSession() self.__session_variables = {} # run teststeps for step in self.teststeps: # update with config variables step.variables.update(self.config.variables) # update with session variables extracted from pre step step.variables.update(self.__session_variables) # parse variables step.variables = parse_variables_mapping( step.variables, self.__project_meta.functions) # run step with allure.step(f"step: {step.name}"): extract_mapping = self.__run_step(step) # save extracted variables to session variables self.__session_variables.update(extract_mapping) self.__duration = time.time() - self.__start_at return self
def __init__(self, config, http_client_session=None): """ run testcase or testsuite. Args: config (dict): testcase/testsuite config dict { "name": "ABC", "variables": {}, "setup_hooks", [], "teardown_hooks", [] } http_client_session (instance): requests.Session(), or locust.client.Session() instance. """ self.verify = config.get("verify", True) self.export = config.get("export") or config.get("output", []) config_variables = config.get("variables", {}) # testcase setup hooks testcase_setup_hooks = config.get("setup_hooks", []) # testcase teardown hooks self.testcase_teardown_hooks = config.get("teardown_hooks", []) self.http_client_session = http_client_session or HttpSession() self.session_context = SessionContext(config_variables) if testcase_setup_hooks: self.do_hook_actions(testcase_setup_hooks, HookTypeEnum.SETUP)
def __init__(self, config, functions, http_client_session=None): """ run testcase or testsuite. Args: config (dict): testcase/testsuite config dict { "name": "ABC", "variables": {}, "setup_hooks", [], "teardown_hooks", [] } http_client_session (instance): requests.Session(), or locust.client.Session() instance. """ base_url = config.get("base_url") self.verify = config.get("verify", True) self.output = config.get("output", []) self.functions = functions self.validation_results = [] # testcase setup hooks testcase_setup_hooks = config.get("setup_hooks", []) # testcase teardown hooks self.testcase_teardown_hooks = config.get("teardown_hooks", []) self.http_client_session = http_client_session or HttpSession(base_url) self.session_context = SessionContext(self.functions) if testcase_setup_hooks: self.do_hook_actions(testcase_setup_hooks, "setup")
def init_config(self, config_dict, level): """ create/update context variables binds @param (dict) config_dict @param (str) level, "testset" or "testcase" testset: { "name": "smoke testset", "path": "tests/data/demo_testset_variables.yml", "requires": [], # optional "function_binds": {}, # optional "import_module_items": [], # optional "variables": [], # optional "request": { "base_url": "http://127.0.0.1:5000", "headers": { "User-Agent": "iOS/2.8.3" } } } testcase: { "name": "testcase description", "requires": [], # optional "function_binds": {}, # optional "import_module_items": [], # optional "variables": [], # optional "request": { "url": "/api/get-token", "method": "POST", "headers": { "Content-Type": "application/json" } }, "json": { "sign": "f1219719911caae89ccc301679857ebfda115ca2" } } @param (str) context level, testcase or testset """ # convert keys in request headers to lowercase #将config字典的内容都转换为小写 config_dict = utils.lower_config_dict_key(config_dict) #初始化context self.context.init_context(level) #提取config_context里的值进行处理,添加到上下文环境里 self.context.config_context(config_dict, level) request_config = config_dict.get('request', {}) #把config_dict里的request字典的值处理为常规类型并返回eg:sn:$sn;parsed: sn:123 parsed_request = self.context.get_parsed_request(request_config, level) base_url = parsed_request.pop("base_url", None) self.http_client_session = self.http_client_session or HttpSession(base_url) return parsed_request
def init_config(self, config_dict, level): """ create/update context variables binds Args: config_dict (dict): level (enum): "testcase" or "teststep" testcase: { "name": "testcase description", "variables": [], # optional "request": { "base_url": "http://127.0.0.1:5000", "headers": { "User-Agent": "iOS/2.8.3" } } } teststep: { "name": "teststep description", "variables": [], # optional "request": { "url": "/api/get-token", "method": "POST", "headers": { "Content-Type": "application/json" } }, "json": { "sign": "f1219719911caae89ccc301679857ebfda115ca2" } } Returns: dict: parsed request dict """ # convert keys in request headers to lowercase config_dict = utils.lower_config_dict_key(config_dict) self.context.init_context_variables(level) variables = config_dict.get('variables') \ or config_dict.get('variable_binds', OrderedDict()) self.context.update_context_variables(variables, level) request_config = config_dict.get('request', {}) parsed_request = self.context.get_parsed_request(request_config, level) base_url = parsed_request.pop("base_url", None) self.http_client_session = self.http_client_session or HttpSession( base_url) return parsed_request
def run_testcase(self, testcase: TestCase) -> "HttpRunner": """run specified testcase Examples: >>> testcase_obj = TestCase(config=TConfig(...), teststeps=[TStep(...)]) >>> HttpRunner().with_project_meta(project_meta).run_testcase(testcase_obj) """ self.__config = testcase.config self.__teststeps = testcase.teststeps # prepare self.__project_meta = self.__project_meta or load_project_meta( self.__config.path ) self.__parse_config(self.__config) self.__start_at = time.time() self.__step_datas: List[StepData] = [] self.__session = self.__session or HttpSession() self.__session_variables = {} # run teststeps for step in self.__teststeps: # override variables # session variables (extracted from pre step) > step variables step.variables.update(self.__session_variables) # step variables > testcase config variables step.variables = override_config_variables( step.variables, self.__config.variables ) # parse variables step.variables = parse_variables_mapping( step.variables, self.__project_meta.functions ) # run step if USE_ALLURE: with allure.step(f"step: {step.name}"): extract_mapping = self.__run_step(step) else: extract_mapping = self.__run_step(step) # save extracted variables to session variables self.__session_variables.update(extract_mapping) self.__duration = time.time() - self.__start_at return self
def init_config(self, config_dict, level): """ create/update context variables binds @param (dict) config_dict @param (str) level, "testset" or "testcase" testset: { "name": "smoke testset", "path": "tests/data/demo_testset_variables.yml", "variables": [], # optional "request": { "base_url": "http://127.0.0.1:5000", "headers": { "User-Agent": "iOS/2.8.3" } } } testcase: { "name": "testcase description", "variables": [], # optional "request": { "url": "/api/get-token", "method": "POST", "headers": { "Content-Type": "application/json" } }, "json": { "sign": "f1219719911caae89ccc301679857ebfda115ca2" } } @param (str) context level, testcase or testset """ # convert keys in request headers to lowercase config_dict = utils.lower_config_dict_key(config_dict) self.context.init_context(level) self.context.config_context(config_dict, level) request_config = config_dict.get('request', {}) parsed_request = self.context.get_parsed_request(request_config, level) base_url = parsed_request.pop("base_url", None) self.http_client_session = self.http_client_session or HttpSession( base_url) return parsed_request
def __init(self): init_logger() self.__config = self.config.struct() self.__session_variables = {} self.__start_at = 0 self.__duration = 0 self.__project_meta = self.__project_meta or load_project_meta( self.__config.path) self.case_id = self.case_id or str(uuid.uuid4()) self.root_dir = self.root_dir or self.__project_meta.RootDir self.__log_path = os.path.join(self.root_dir, "logs", f"{self.case_id}.run.log") self.__step_results.clear() self.session = self.session or HttpSession() self.parser = self.parser or Parser(self.__project_meta.functions)
def __run_step_request(self, step: TStep): """run teststep: request""" step_data = StepData(name=step.name) # parse prepare_upload_step(step, self.__project_meta.functions) request_dict = step.request.dict() request_dict.pop("upload", None) parsed_request_dict = parse_data(request_dict, step.variables, self.__project_meta.functions) # prepare arguments method = parsed_request_dict.pop("method") url_path = parsed_request_dict.pop("url") url = build_url(self.config.base_url, url_path) parsed_request_dict["json"] = parsed_request_dict.pop("req_json", {}) logger.info(f"{method} {url}") logger.debug(f"request kwargs(raw): {parsed_request_dict}") # request self.__session = self.__session or HttpSession() resp = self.__session.request(method, url, **parsed_request_dict) resp_obj = ResponseObject(resp) def log_req_resp_details(): err_msg = "\n{} DETAILED REQUEST & RESPONSE {}\n".format( "*" * 32, "*" * 32) # log request err_msg += "====== request details ======\n" err_msg += f"url: {url}\n" err_msg += f"method: {method}\n" headers = parsed_request_dict.pop("headers", {}) err_msg += f"headers: {headers}\n" for k, v in parsed_request_dict.items(): v = utils.omit_long_data(v) err_msg += f"{k}: {repr(v)}\n" err_msg += "\n" # log response err_msg += "====== response details ======\n" err_msg += f"status_code: {resp.status_code}\n" err_msg += f"headers: {resp.headers}\n" err_msg += f"body: {repr(resp.text)}\n" logger.error(err_msg) # extract extractors = step.extract extract_mapping = resp_obj.extract(extractors) step_data.export = extract_mapping variables_mapping = step.variables variables_mapping.update(extract_mapping) # validate validators = step.validators try: resp_obj.validate(validators, variables_mapping, self.__project_meta.functions) self.__session.data.success = True except ValidationFailure: self.__session.data.success = False log_req_resp_details() raise finally: # save request & response meta data self.__session.data.validators = resp_obj.validation_results self.success &= self.__session.data.success # save step data step_data.success = self.__session.data.success step_data.data = self.__session.data return step_data
def setUp(self): super(TestHttpClient, self).setUp() self.api_client = HttpSession(self.host) self.headers = self.get_authenticated_headers() self.reset_all()
class TestHttpClient(ApiServerUnittest): def setUp(self): super(TestHttpClient, self).setUp() self.api_client = HttpSession(self.host) self.headers = self.get_authenticated_headers() self.reset_all() def tearDown(self): super(TestHttpClient, self).tearDown() def reset_all(self): url = "%s/api/reset-all" % self.host headers = self.get_authenticated_headers() return self.api_client.get(url, headers=headers) def test_request_with_full_url(self): url = "%s/api/users/1000" % self.host data = {'name': 'user1', 'password': '******'} resp = self.api_client.post(url, json=data, headers=self.headers) self.assertEqual(201, resp.status_code) self.assertEqual(True, resp.json()['success']) def test_request_without_base_url(self): url = "/api/users/1000" data = {'name': 'user1', 'password': '******'} resp = self.api_client.post(url, json=data, headers=self.headers) self.assertEqual(201, resp.status_code) self.assertEqual(True, resp.json()['success']) def test_request_post_data(self): url = "/api/users/1000" data = {'name': 'user1', 'password': '******'} resp = self.api_client.post(url, json=data, headers=self.headers) # b'{"name": "user1", "password": "******"}' self.assertIn(b'"name": "user1"', resp.request.body) self.assertIn(b'"password": "******"', resp.request.body) resp = self.api_client.post(url, data=data, headers=self.headers) # name=user1&password=123456 self.assertIn("name=user1", resp.request.body) self.assertIn("&", resp.request.body) self.assertIn("password=123456", resp.request.body) def test_request_with_cookies(self): url = "/api/users/1000" data = {'name': 'user1', 'password': '******'} cookies = {"a": "1", "b": "2"} resp = self.api_client.get(url, cookies=cookies, headers=self.headers) self.assertEqual(resp.request._cookies["a"], "1") self.assertEqual(resp.request._cookies["b"], "2") def test_request_redirect(self): url = "{}/redirect-to?url=https%3A%2F%2Fdebugtalk.com&status_code=302".format( HTTPBIN_SERVER) headers = {"accept: text/html"} cookies = {"a": "1", "b": "2"} resp = self.api_client.get(url, cookies=cookies, headers=self.headers) raw_request = resp.history[0].request self.assertEqual(raw_request._cookies["a"], "1") self.assertEqual(raw_request._cookies["b"], "2") redirect_request = resp.request self.assertEqual(redirect_request.url, "https://debugtalk.com") self.assertEqual(redirect_request._cookies["a"], "1") self.assertEqual(redirect_request._cookies["b"], "2")
def run_testcase(self, testcase: TestCase) -> "HttpRunner": """run specified testcase Examples: >>> testcase_obj = TestCase(config=TConfig(...), teststeps=[TStep(...)]) >>> HttpRunner().with_project_meta(project_meta).run_testcase(testcase_obj) """ self.__config = testcase.config self.__teststeps = testcase.teststeps # prepare self.__project_meta = self.__project_meta or load_project_meta( self.__config.path) self.__parse_config(self.__config) self.__start_at = time.time() self.__step_datas: List[StepData] = [] self.__session = self.__session or HttpSession() # save extracted variables of teststeps extracted_variables: VariablesMapping = {} # run teststeps for step in self.__teststeps: # override variables # step variables > extracted variables from previous steps step.variables = merge_variables(step.variables, extracted_variables) # step variables > testcase config variables step.variables = merge_variables(step.variables, self.__config.variables) # parse variables step.variables = parse_variables_mapping( step.variables, self.__project_meta.functions) while True: # run step if USE_ALLURE: with allure.step(f"step: {step.name}"): extract_mapping = self.__run_step(step) else: extract_mapping = self.__run_step(step) if step.retry_whens: variables_mapping = step.variables variables_mapping.update(extract_mapping) try: response = step.variables.get( 'response') or ResponseObject(requests.Response()) if isinstance(response, ResponseObject): response.validate(step.retry_whens, variables_mapping, self.__project_meta.functions) else: break except ValidationFailure: break else: break # save extracted variables to session variables extracted_variables.update(extract_mapping) self.__session_variables.update(extracted_variables) self.__duration = time.time() - self.__start_at return self
def run_testcase(self, testcase: TestCase) -> "HttpRunner": """run specified testcase Examples: >>> testcase_obj = TestCase(config=TConfig(...), teststeps=[TStep(...)]) >>> HttpRunner().with_project_meta(project_meta).run_testcase(testcase_obj) """ self.__config = testcase.config self.__teststeps = testcase.teststeps # prepare self.__project_meta = self.__project_meta or load_project_meta( self.__config.path) self.__parse_config(self.__config) self.__start_at = time.time() self.__step_datas: List[StepData] = [] self.__session = self.__session or HttpSession() # save extracted variables of teststeps extracted_variables: VariablesMapping = {} # run teststeps for step in self.__teststeps: # override variables # step variables > extracted variables from previous steps step.variables = merge_variables(step.variables, extracted_variables) # step variables > testcase config variables step.variables = merge_variables(step.variables, self.__config.variables) # parse variables step.variables = parse_variables_mapping( step.variables, self.__project_meta.functions) step.skipif = parse_data(step.skipif, step.variables, self.__project_meta.functions) # 跳过满足条件的步骤 logger.debug(f"[跳过步骤] skipif={step.skipif}") if step.skipif == '': continue if step.skipif and eval(step.skipif): logger.debug( f"[满足条件,跳过步骤] skipif={step.skipif} | step_name={step.name}" ) continue # run step if USE_ALLURE: with allure.step(f"step: {step.name}"): extract_mapping = self.__run_step(step) else: extract_mapping = self.__run_step(step) # 每运行一个步骤就重新加载公共变量 logger.debug(f"step.variables={step.variables}") step.variables.clear() # save extracted variables to session variables extracted_variables.update(extract_mapping) self.__session_variables.update(extracted_variables) self.__duration = time.time() - self.__start_at return self
def setUp(self): self.session = HttpSession()
class TestHttpSession(unittest.TestCase): def setUp(self): self.session = HttpSession() def test_request_http(self): self.session.request("get", "http://httpbin.org/get") address = self.session.data.address self.assertGreater(len(address.server_ip), 0) self.assertEqual(address.server_port, 80) self.assertGreater(len(address.client_ip), 0) self.assertGreater(address.client_port, 10000) def test_request_https(self): self.session.request("get", "https://httpbin.org/get") address = self.session.data.address self.assertGreater(len(address.server_ip), 0) self.assertEqual(address.server_port, 443) self.assertGreater(len(address.client_ip), 0) self.assertGreater(address.client_port, 10000) def test_request_http_allow_redirects(self): self.session.request( "get", "http://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com", allow_redirects=True, ) address = self.session.data.address self.assertNotEqual(address.server_ip, "N/A") self.assertEqual(address.server_port, 443) self.assertNotEqual(address.server_ip, "N/A") self.assertGreater(address.client_port, 10000) def test_request_https_allow_redirects(self): self.session.request( "get", "https://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com", allow_redirects=True, ) address = self.session.data.address self.assertNotEqual(address.server_ip, "N/A") self.assertEqual(address.server_port, 443) self.assertNotEqual(address.server_ip, "N/A") self.assertGreater(address.client_port, 10000) def test_request_http_not_allow_redirects(self): self.session.request( "get", "http://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com", allow_redirects=False, ) address = self.session.data.address self.assertEqual(address.server_ip, "N/A") self.assertEqual(address.server_port, 0) self.assertEqual(address.client_ip, "N/A") self.assertEqual(address.client_port, 0) def test_request_https_not_allow_redirects(self): self.session.request( "get", "https://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com", allow_redirects=False, ) address = self.session.data.address self.assertEqual(address.server_ip, "N/A") self.assertEqual(address.server_port, 0) self.assertEqual(address.client_ip, "N/A") self.assertEqual(address.client_port, 0)