Exemple #1
0
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)
Exemple #2
0
    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)
Exemple #4
0
    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")
Exemple #5
0
    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
Exemple #6
0
    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
Exemple #7
0
    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
Exemple #8
0
    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
Exemple #9
0
    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)
Exemple #10
0
    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()
Exemple #12
0
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")
Exemple #13
0
    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
Exemple #14
0
    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
Exemple #15
0
 def setUp(self):
     self.session = HttpSession()
Exemple #16
0
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)