コード例 #1
0
    def extract_field(self, field):
        """ extract value from requests.Response.
        """
        msg = "extract field: {}".format(field)

        try:
            if text_extractor_regexp_compile.match(field):
                value = self._extract_field_with_regex(field)
            else:
                value = self._extract_field_with_delimiter(field)

            msg += "\t=> {}".format(value)
            logger.log_debug(msg)

        # TODO: unify ParseResponseError type
        except (exception.ParseResponseError, TypeError):
            logger.log_error("failed to extract field: {}".format(field))
            raise

        return value
コード例 #2
0
ファイル: report.py プロジェクト: zjh-coder/HttpRunner
def render_html_report(summary, report_template=None, report_dir=None):
    """ render html report with specified report name and template

    Args:
        report_template (str): specify html report template path
        report_dir (str): specify html report save directory

    """
    if not report_template:
        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(report_template))

    logger.log_info("Start to render Html report ...")

    report_dir = report_dir or os.path.join(os.getcwd(), "reports")
    if not os.path.isdir(report_dir):
        os.makedirs(report_dir)

    start_at_timestamp = int(summary["time"]["start_at"])
    summary["time"]["start_datetime"] = datetime.fromtimestamp(
        start_at_timestamp).strftime('%Y-%m-%d %H:%M:%S')

    report_path = os.path.join(report_dir,
                               "{}.html".format(start_at_timestamp))

    with io.open(report_template, "r", encoding='utf-8') as fp_r:
        template_content = fp_r.read()
        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
コード例 #3
0
def print_output(output):
    if not output:
        return

    content = "\n================== Output ==================\n"
    content += '{:<16}:  {:<}\n'.format("Variable", "Value")
    content += '{:<16}:  {:<}\n'.format("--------", "-----")

    for variable, value in output.items():

        if PYTHON_VERSION == 2:
            if isinstance(variable, unicode):
                variable = variable.encode("utf-8")
            if isinstance(value, unicode):
                value = value.encode("utf-8")

        content += '{:<16}:  {:<}\n'.format(variable, value)

    content += "============================================\n"

    logger.log_debug(content)
コード例 #4
0
ファイル: client.py プロジェクト: hewei198711/httprunner2.5.7
 def _send_request_safe_mode(self, method, url, **kwargs):
     """
     Send a HTTP request, and catch any exception that might occur due to connection problems.
     发送一个HTTP请求,并捕捉任何可能由于连接问题而发生的异常
     Safe mode has been removed from requests 1.x.
     安全模式已经从请求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使用这个status_code,内容将返回None
         resp.request = Request(method, url).prepare()
         return resp
コード例 #5
0
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
コード例 #6
0
    def do_hook_actions(self, actions, hook_type):
        """ call hook actions.

        Args:
            actions (list): each action in actions list maybe in two format.

                format1 (dict): assignment, the value returned by hook function will be assigned to variable.
                    {"var": "${func()}"}
                format2 (str): only call hook functions.
                    ${func()}

            hook_type (enum): setup/teardown

        """
        logger.log_debug("call {} hook actions.".format(hook_type))
        for action in actions:

            if isinstance(action, dict) and len(action) == 1:
                # format 1
                # {"var": "${func()}"}
                var_name, hook_content = list(action.items())[0]
                logger.log_debug("assignment with hook: {} = {}".format(
                    var_name, hook_content))
                self.session_context.update_test_variables(
                    var_name, self.session_context.eval_content(hook_content))
            else:
                # format 2
                logger.log_debug("call hook function: {}".format(action))
                # TODO: check hook function if valid
                self.session_context.eval_content(action)
コード例 #7
0
ファイル: runner.py プロジェクト: hewei198711/httprunner2.5.7
    def do_hook_actions(self, actions, hook_type):
        """ call hook actions.

        Args:
            actions (list): each action in actions list maybe in two format.动作列表中的每个动作可能有两种格式

                format1 (dict): assignment, the value returned by hook function will be assigned to variable.
                                赋值,钩子函数返回的值将被赋给变量
                    {"var": "${func()}"}
                format2 (str): only call hook functions.只调用钩子函数
                    ${func()}

            hook_type (HookTypeEnum): setup/teardown

        """
        logger.log_debug("call {} hook actions.".format(hook_type.name))
        for action in actions:

            if isinstance(action, dict) and len(action) == 1:
                # format 1
                # {"var": "${func()}"}
                var_name, hook_content = list(action.items())[0]
                hook_content_eval = self.session_context.eval_content(
                    hook_content)
                logger.log_debug("assignment with hook: {} = {} => {}".format(
                    var_name, hook_content, hook_content_eval))
                self.session_context.update_test_variables(
                    var_name, hook_content_eval)
            else:
                # format 2
                logger.log_debug("call hook function: {}".format(action))
                # TODO: check hook function if valid 检查钩函数是否有效
                self.session_context.eval_content(action)
コード例 #8
0
def load_dot_env_file():
    """ load .env file, .env file should be located in project working directory.

    Returns:
        dict: environment 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(
            ".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:
            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
コード例 #9
0
ファイル: report.py プロジェクト: zhangkg/HttpRunner
    def render_html_report(self, 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 = self.default_report_template_path
            logger.log_debug("No html report template specified, use default.")
        else:
            logger.log_info("render with html report template: {}".format(html_report_template))

        with open(html_report_template, "r") as fp:
            template_content = fp.read()

        summary = self.summary
        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)

        report_path = os.path.join(report_dir_path, html_report_name)
        with io.open(report_path, 'w', encoding='utf-8') as fp:
            rendered_content = Template(template_content).render(summary)
            fp.write(rendered_content)

        logger.log_info("Generated Html report: {}".format(report_path))

        return report_path
コード例 #10
0
ファイル: response.py プロジェクト: luqingfeng01/pycharm
    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
コード例 #11
0
    def do_validation(self, validator_dict):
        """ validate with functions
        """
        # TODO: move comparator uniform to init_test_suites
        comparator = utils.get_uniform_comparator(validator_dict["comparator"])
        validate_func = self.testcase_parser.get_bind_function(comparator)

        if not validate_func:
            raise exceptions.FunctionNotFound(
                "comparator not found: {}".format(comparator))

        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)
コード例 #12
0
def load_test_dependency_map_by_path(path):
    """Get test_dependency_map by the specified path(Relative or Absolute path).
    Example:
        dict1,dict2 = load_test_dependency_map_by_path('../my_test')
        print('{},{}'.format(dict1,dict2))
        >> {'nested_para': [''], 'nested_5': [''], 'nested_1': ['nested_3']}, {'nested':'/Users/..../xxx.yml'}

    @param path    String. Relative or Absolute path is accepted.

    @return        List. Two dict in the list.
    """

    all_result = TestcaseLoader.load_testsets_by_path(path)
    logger.log_debug('{}'.format(all_result))

    testcase_dep_dict = collections.defaultdict(list)
    testcase_path_dict = collections.defaultdict(list)

    for result in all_result:

        testcase_name = result['testcases'][0].get('name', '')
        raw_testcase_dep = result['testcases'][0].get('dependent', '')
        testcase_path = result['config'].get('path', '')

        if not testcase_dep_dict[testcase_name]:
            if not isinstance(raw_testcase_dep, (list, set)):
                testcase_list = raw_testcase_dep.split(',')
                testcase_dep_dict[testcase_name].extend(testcase_list)
            else:
                testcase_dep_dict[testcase_name].extend(raw_testcase_dep)
        else:
            logger.log_warning("Duplicate testcase found!! Please check-->"\
                             "testcase_name:{},testcase_path:{}".format(testcase_name,testcase_path))
        #    raise Exception("Duplicate testcase found!!")   #根据需要决定是否抛出异常

        testcase_path_dict[testcase_name] = testcase_path

    return testcase_dep_dict, testcase_path_dict
コード例 #13
0
def print_output(outputs):

    if not outputs:
        return

    content = "\n================== Variables & Output ==================\n"
    content += '{:<6} | {:<16} :  {:<}\n'.format("Type", "Variable", "Value")
    content += '{:<6} | {:<16} :  {:<}\n'.format("-" * 6, "-" * 16, "-" * 27)

    def prepare_content(var_type, in_out):
        content = ""
        for variable, value in in_out.items():

            if is_py2:
                if isinstance(variable, unicode):
                    variable = variable.encode("utf-8")
                if isinstance(value, unicode):
                    value = value.encode("utf-8")

            content += '{:<6} | {:<16} :  {:<}\n'.format(
                var_type, variable, value)

        return content

    for output in outputs:
        _in = output["in"]
        _out = output["out"]

        if not _out:
            continue

        content += prepare_content("Var", _in)
        content += "\n"
        content += prepare_content("Out", _out)
        content += "-" * 56 + "\n"

    logger.log_debug(content)
コード例 #14
0
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
コード例 #15
0
    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": [
                    {"error_code": "errorcode"},
                    {"ID": "Data.AreaList.Area.ID"},
                ],              # 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
コード例 #16
0
 def log_print(req_resp_dict, r_type):
     msg = "\n================== {} details ==================\n".format(
         r_type)
     for key, value in req_resp_dict[r_type].items():
         msg += "{:<16} : {}\n".format(key, repr(value))
     logger.log_debug(msg)
コード例 #17
0
    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.
        """
        # record original request info
        self.meta_data["method"] = method
        self.meta_data["url"] = url
        self.meta_data["request_time"] = time.time()

        # prepend url with hostname unless it's already an absolute URL
        url = self._build_url(url)

        kwargs.setdefault("timeout", 120)
        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

        # record actual request info
        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

        # record response info
        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

        # log response details in debug mode
        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
コード例 #18
0
ファイル: runner.py プロジェクト: hewei198711/httprunner2.5.7
    def _run_test(self, test_dict):
        """ run single teststep. 运行每一个测试步骤

        Args:
            test_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"
                        },
                        "json": {"name": "user", "password": "******"}
                    },
                    "extract": {},              # optional可选的
                    "validate": [],             # optional可选的
                    "setup_hooks": [],          # optional可选的
                    "teardown_hooks": []        # optional可选的
                }

        Raises:
            exceptions.ParamsError 参数错误
            exceptions.ValidationFailure 验证失败
            exceptions.ExtractFailure 提取失败

        """
        # clear meta data first to ensure independence for each test 首先清除元数据,确保每个测试的独立性
        self.__clear_test_data()

        # check skip 检查跳过
        self._handle_skip_feature(test_dict)

        # prepare 准备参数
        test_dict = utils.lower_test_dict_keys(test_dict)
        test_variables = test_dict.get("variables", {})
        self.session_context.init_test_variables(test_variables)

        # teststep name
        test_name = self.session_context.eval_content(test_dict.get(
            "name", ""))

        # parse test request
        raw_request = test_dict.get('request', {})
        parsed_test_request = self.session_context.eval_content(raw_request)
        self.session_context.update_test_variables("request",
                                                   parsed_test_request)

        # setup hooks
        setup_hooks = test_dict.get("setup_hooks", [])
        if setup_hooks:
            self.do_hook_actions(setup_hooks, HookTypeEnum.SETUP)

        # prepend url with base_url unless it's already an absolute URL 在url前面加上base_url,除非它已经是一个绝对url
        url = parsed_test_request.pop('url')
        base_url = self.session_context.eval_content(
            test_dict.get("base_url", ""))
        parsed_url = utils.build_url(base_url, url)

        try:
            method = parsed_test_request.pop('method')
            parsed_test_request.setdefault("verify", self.verify)
            group_name = parsed_test_request.pop("group", None)
        except KeyError:
            raise exceptions.ParamsError("URL or METHOD missed!")

        logger.log_info("{method} {url}".format(method=method, url=parsed_url))
        logger.log_debug(
            "request kwargs(raw): {kwargs}".format(kwargs=parsed_test_request))

        # request
        resp = self.http_client_session.request(method,
                                                parsed_url,
                                                name=(group_name or test_name),
                                                **parsed_test_request)
        resp_obj = response.ResponseObject(resp)

        def log_req_resp_details():
            err_msg = "{} DETAILED REQUEST & RESPONSE {}\n".format(
                "*" * 32, "*" * 32)

            # log request
            err_msg += "====== request details ======\n"
            err_msg += "url: {}\n".format(parsed_url)
            err_msg += "method: {}\n".format(method)
            err_msg += "headers: {}\n".format(
                parsed_test_request.pop("headers", {}))
            for k, v in parsed_test_request.items():
                v = utils.omit_long_data(v)
                err_msg += "{}: {}\n".format(k, repr(v))

            err_msg += "\n"

            # log response
            err_msg += "====== response details ======\n"
            err_msg += "status_code: {}\n".format(resp_obj.status_code)
            err_msg += "headers: {}\n".format(resp_obj.headers)
            err_msg += "body: {}\n".format(repr(resp_obj.text))
            logger.log_error(err_msg)

        # teardown hooks
        teardown_hooks = test_dict.get("teardown_hooks", [])
        if teardown_hooks:
            self.session_context.update_test_variables("response", resp_obj)
            self.do_hook_actions(teardown_hooks, HookTypeEnum.TEARDOWN)
            self.http_client_session.update_last_req_resp_record(resp_obj)

        # extract
        extractors = test_dict.get("extract", {})
        try:
            extracted_variables_mapping = resp_obj.extract_response(extractors)
            self.session_context.update_session_variables(
                extracted_variables_mapping)
        except (exceptions.ParamsError,
                exceptions.ExtractFailure):  #Extract Failure提取失败
            log_req_resp_details()
            raise

        # validate
        validators = test_dict.get("validate") or test_dict.get(
            "validators") or []
        validate_script = test_dict.get("validate_script", [])
        if validate_script:
            validators.append({
                "type": "python_script",
                "script": validate_script
            })

        validator = Validator(self.session_context, resp_obj)
        try:
            validator.validate(validators)
        except exceptions.ValidationFailure:
            log_req_resp_details()
            raise
        finally:
            self.validation_results = validator.validation_results
コード例 #19
0
ファイル: runner.py プロジェクト: zongfangfang/HttpRunner
 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)
コード例 #20
0
    def dependent_runner(self, Origion_dic):

        # if self.login_flag:
        #     for item in Origion_dic:
        #         if item.get("test",""):
        #             if item.get("test","").get("name","") in self.dependent_root:
        #                 logger.log_debug('Inside dependent_runner login_flag-------------````````````````````````````')
        #                 logger.log_debug('test name:{}'.format(item.get("test","").get("name","")))
        #                 logger.log_debug('global_variables: {}'.format(self.global_variables))
        #                 logger.log_debug('http_client_session: {}'.format(self.http_client_session))
        #                 return {}

        dependent_variables = defaultdict(list)
        dependent_list = []
        logger.log_debug('Origion_dic: {}'.format(Origion_dic))
        for item in Origion_dic:
            if item.get("test", ""):
                testcase_name = item.get("test", "").get("name", "")
                idempotency = item.get("test", "").pop("idempotency", "")
                session = item.get("test", "").pop("session", "")
                if session:
                    if not self.http_client_session_dict.get(
                            testcase_name, ""):
                        self.http_client_session_dict[
                            testcase_name] = self.http_client_session_dict[
                                "default"]
                    self.http_client_session_dict[
                        "default"] = self.http_client_session_dict[
                            testcase_name]
                if check_case_result(testcase_name):
                    item.get("test", "")["skip"] = "case fail"
                else:
                    last_result = get_case_result(testcase_name)
                    if isinstance(last_result, dict):
                        return last_result
                raw_testcase_dep = item.get("test", "").pop("dependent", "")

                if raw_testcase_dep and not isinstance(raw_testcase_dep,
                                                       (list, set)):
                    testcase_list = raw_testcase_dep.split(',')
                    dependent_list.extend(testcase_list)
                else:
                    dependent_list.extend(raw_testcase_dep)

        for dependent_testcase_name in dependent_list:
            for key, value in self.dependent_runner(
                    self.get_dependent_testcase(
                        dependent_testcase_name)).items():
                if key.startswith("global_"):
                    self.global_variables[key] = value  #所有全testsuit的参数使用k-v?
                else:
                    if dependent_variables.get(key, ""):
                        if not isinstance(dependent_variables.get(key, ""),
                                          list):
                            temp = dependent_variables[key]
                            dependent_variables[key] = [temp]
                        dependent_variables[key].append(value)
                    else:
                        dependent_variables[key] = value

        for key, value in self.global_variables.items():
            if not dependent_variables.get(key, ""):
                dependent_variables[key] = value

        for item in Origion_dic:
            if item.get("test", ""):
                if any([
                        check_case_result(dependent_case)
                        for dependent_case in dependent_list
                ]):
                    item.get("test", "")["skip"] = "dependent case fail"
            if item.get("config", ""):
                for variables in (item.get("config", "").pop("variables", "")):
                    for key, value in variables.items():
                        dependent_variables[key] = value
        logger.log_debug('dependent_variabls------:')
        logger.log_debug('{}'.format(dependent_variables))
        dic = dependency_parser.load_test_file(Origion_dic)
        logger.log_debug('dic-----:')
        logger.log_debug('{}'.format(dic))
        testcase = YDHTestSuite(
            dic,
            http_client_session=self.http_client_session_dict["default"],
            variables_mapping=dependent_variables)
        self.runner.run(testcase)
        logger.log_debug('testcase output------:')
        logger.log_debug('{}'.format(testcase.output))
        if idempotency:
            set_case_result(testcase_name, testcase.output[0].get('out', ""))
        return testcase.output[0].get('out', "")
コード例 #21
0
 def do_hook_actions(self, actions):
     for action in actions:
         logger.log_debug("call hook: {}".format(action))
         self.context.eval_content(action)
コード例 #22
0
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("Set OS environment variable: {}".format(variable))
コード例 #23
0
 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)
コード例 #24
0
    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)
        logger.log_info("{method} {url}".format(method=method, url=url))
        logger.log_debug("request kwargs(raw): {kwargs}".format(kwargs=kwargs))

        # set up pre_request hook for attaching meta data to the request object
        self.meta_data["method"] = method

        if "httpntlmauth" in kwargs:
            from requests_ntlm import HttpNtlmAuth
            auth_account = kwargs.pop("httpntlmauth")
            kwargs["auth"] = HttpNtlmAuth(auth_account["username"],
                                          auth_account["password"])

        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"] = int(
            (time.time() - self.meta_data["request_time"]) * 1000)
        self.meta_data["elapsed"] = response.elapsed.total_seconds()

        self.meta_data["url"] = (response.history and response.history[0] or response) \
            .request.path_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
        self.meta_data["response_body"] = response.text

        logger.log_debug("response status_code: {}".format(
            self.meta_data["status_code"]))
        logger.log_debug("response headers: {}".format(
            self.meta_data["response_headers"]))
        logger.log_debug("response body: {}".format(
            self.meta_data["response_body"]))

        # 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, response_length: {} bytes"""
                .format(self.meta_data["status_code"],
                        self.meta_data["response_time"],
                        self.meta_data["content_size"]))

        return response
コード例 #25
0
    def _run_dubbo_test(self, test_dict):
        # clear meta data first to ensure independence for each test
        self.__clear_test_data()

        # check skip
        self._handle_skip_feature(test_dict)

        # prepare
        test_dict = utils.lower_test_dict_keys(test_dict)
        test_variables = test_dict.get("variables", {})
        self.session_context.init_test_variables(test_variables)

        # parse test request
        raw_request = test_dict.get('request', {})
        parsed_test_request = self.session_context.eval_content(raw_request)
        self.session_context.update_test_variables("request",
                                                   parsed_test_request)

        # prepend url with base_url unless it's already an absolute URL
        host = parsed_test_request.pop('host', '')
        port = parsed_test_request.pop('port', '')

        # setup hooks
        setup_hooks = test_dict.get("setup_hooks", [])
        if setup_hooks:
            self.do_hook_actions(setup_hooks, HookTypeEnum.SETUP)

        try:
            service = parsed_test_request.pop('service')
            method = parsed_test_request.pop('method')
            params = parsed_test_request.pop("params", "")
            parsed_test_request.setdefault("verify", self.verify)
            group_name = parsed_test_request.pop("group", None)
        except KeyError:
            raise exceptions.ParamsError("SERVICE or METHOD missed!")

        logger.log_info("{host}:{port} {service}.{method}".format(
            host=host, port=port, service=service, method=method))
        logger.log_debug(
            "request kwargs(raw): {kwargs}".format(kwargs=parsed_test_request))

        # request
        resp = self.dubbo_client_session.request(
            host,
            port,
            service,
            method,
            params,
            name=(group_name or self.session_context.eval_content(
                test_dict.get("name", ""))),
            **parsed_test_request)
        # TODO: responseObject
        resp_obj = DubboResponseObject(resp)

        def log_req_resp_details():
            err_msg = "{} DETAILED REQUEST & RESPONSE {}\n".format(
                "*" * 32, "*" * 32)

            # log request
            err_msg += "====== request details ======\n"
            err_msg += "host: {}\n".format(host)
            err_msg += "port: {}\n".format(port)
            err_msg += "service: {}\n".format(service)
            err_msg += "method: {}\n".format(method)
            err_msg += "params: {}\n".format(params)
            for k, v in parsed_test_request.items():
                v = utils.omit_long_data(v)
                err_msg += "{}: {}\n".format(k, repr(v))

            err_msg += "\n"

            # log response
            err_msg += "====== response details ======\n"
            err_msg += "body: {}\n".format(repr(resp_obj.json))
            logger.log_error(err_msg)

        # teardown hooks
        teardown_hooks = test_dict.get("teardown_hooks", [])
        if teardown_hooks:
            self.session_context.update_test_variables("response", resp_obj)
            self.do_hook_actions(teardown_hooks, HookTypeEnum.TEARDOWN)
            self.dubbo_client_session.update_last_req_resp_record(resp_obj)

        # extract
        extractors = test_dict.get("extract", {})
        try:
            extracted_variables_mapping = resp_obj.extract_response(extractors)
            self.session_context.update_session_variables(
                extracted_variables_mapping)
        except (exceptions.ParamsError, exceptions.ExtractFailure):
            log_req_resp_details()
            raise

        # validate
        validators = test_dict.get("validate") or test_dict.get(
            "validators") or []
        validate_script = test_dict.get("validate_script", [])
        if validate_script:
            validators.append({
                "type": "python_script",
                "script": validate_script
            })

        validator = Validator(self.session_context, resp_obj)
        try:
            validator.validate(validators)
        except exceptions.ValidationFailure:
            log_req_resp_details()
            raise

        return validator.validation_results
コード例 #26
0
    def _run_test_once(self, test_dict, wait_validators=None):
        # clear meta data first to ensure independence for each test
        self.__clear_test_data()

        # check skip
        self._handle_skip_feature(test_dict)

        # prepare
        test_dict = utils.lower_test_dict_keys(test_dict)
        test_variables = test_dict.get("variables", {})
        self.session_context.init_test_variables(test_variables)

        # teststep name
        test_name = test_dict.get("name", "")

        # parse test request
        raw_request = test_dict.get('request', {})
        parsed_test_request = self.session_context.eval_content(raw_request)
        self.session_context.update_test_variables("request",
                                                   parsed_test_request)

        # setup hooks
        setup_hooks = test_dict.get("setup_hooks", [])
        if setup_hooks:
            self.do_hook_actions(setup_hooks, "setup")

        try:
            url = parsed_test_request.pop('url')
            method = parsed_test_request.pop('method')
            parsed_test_request.setdefault("verify", self.verify)
            group_name = parsed_test_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_test_request))

        # request
        resp = self.http_client_session.request(method,
                                                url,
                                                name=(group_name or test_name),
                                                **parsed_test_request)
        resp_obj = response.ResponseObject(resp)

        # teardown hooks
        teardown_hooks = test_dict.get("teardown_hooks", [])
        if teardown_hooks:
            self.session_context.update_test_variables("response", resp_obj)
            self.do_hook_actions(teardown_hooks, "teardown")

        # extract
        extractors = test_dict.get("extract", {})
        extracted_variables_mapping = resp_obj.extract_response(extractors)
        self.session_context.update_session_variables(
            extracted_variables_mapping)

        # validate
        if wait_validators:
            wait_validate_pass, wait_failures = self.session_context.validate(
                wait_validators, resp_obj)
            if not wait_validate_pass:
                # err_msg = "{} DETAILED REQUEST & RESPONSE {}\n".format("*" * 32, "*" * 32)
                #
                # # log request
                # err_msg += "====== request details ======\n"
                # err_msg += "url: {}\n".format(url)
                # err_msg += "method: {}\n".format(method)
                # err_msg += "headers: {}\n".format(parsed_test_request.pop("headers", {}))
                # for k, v in parsed_test_request.items():
                #     v = utils.omit_long_data(v)
                #     err_msg += "{}: {}\n".format(k, repr(v))

                # err_msg += "\n"

                # log response
                # err_msg += "====== response details ======\n"
                # err_msg += "status_code: {}\n".format(resp_obj.status_code)
                # err_msg += "headers: {}\n".format(resp_obj.headers)
                # err_msg += "body: {}\n".format(repr(resp_obj.text))
                # logger.log_error("During the wait: \n" + err_msg)
                logger.log_error(
                    "====== During the wait: the expect conditions can not Meet the requirements! ======"
                )
                failures_string = "\n".join(
                    [failure for failure in wait_failures])
                self.validation_results = self.session_context.validation_results
                raise exceptions.ValidationFailure(failures_string)

        validators = test_dict.get("validate", [])
        validate_pass, failures = self.session_context.validate(
            validators, resp_obj)
        self.validation_results = self.session_context.validation_results

        # try:
        #     validate_pass, failures = self.session_context.validate(validators, resp_obj)

        # except (exceptions.ParamsError, exceptions.ValidationFailure, exceptions.ExtractFailure):
        #     err_msg = "{} DETAILED REQUEST & RESPONSE {}\n".format("*" * 32, "*" * 32)
        #
        #     # log request
        #     err_msg += "====== request details ======\n"
        #     err_msg += "url: {}\n".format(url)
        #     err_msg += "method: {}\n".format(method)
        #     err_msg += "headers: {}\n".format(parsed_test_request.pop("headers", {}))
        #     for k, v in parsed_test_request.items():
        #         v = utils.omit_long_data(v)
        #         err_msg += "{}: {}\n".format(k, repr(v))
        #
        #     err_msg += "\n"
        #
        #     # log response
        #     err_msg += "====== response details ======\n"
        #     err_msg += "status_code: {}\n".format(resp_obj.status_code)
        #     err_msg += "headers: {}\n".format(resp_obj.headers)
        #     err_msg += "body: {}\n".format(repr(resp_obj.text))
        #     logger.log_error(err_msg)

        # raise

        # finally:
        #     self.validation_results = self.session_context.validation_results

        return validate_pass, failures
コード例 #27
0
    def run_test(self, testcase_dict):
        """ run single testcase.
        @param (dict) testcase_dict
            {
                "name": "testcase 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
            }
        @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 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 = 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,context=self.context)
        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 (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
コード例 #28
0
    def validate(self, validators):
        """ make validation with comparators
        """
        self.validation_results = {}
        if not validators:
            return

        logger.log_debug("start to validate.")

        validate_pass = True
        failures = []

        for validator in validators:

            if isinstance(validator, dict) and validator.get("type") == "python_script":
                script = self.session_context.eval_content(validator["script"])
                validator_dict, ex = self.validate_script(script)
                if ex:
                    validate_pass = False
                    failures.append(ex)

                self.validation_results["validate_script"] = validator_dict
                continue

            if "validate_extractor" not in self.validation_results:
                self.validation_results["validate_extractor"] = []

            # validator should be LazyFunction object
            if not isinstance(validator, parser.LazyFunction):
                raise exceptions.ValidationFailure(
                    "validator should be parsed first: {}".format(validators))

            # evaluate validator args with context variable mapping.
            validator_args = validator.get_args()
            check_item, expect_item = validator_args
            check_value = self.__eval_validator_check(check_item)
            expect_value = self.__eval_validator_expect(expect_item)
            validator.update_args([check_value, expect_value])

            comparator = validator.func_name
            validator_dict = {
                "comparator": comparator,
                "check": check_item,
                "check_value": check_value,
                "expect": expect_item,
                "expect_value": expect_value
            }
            validate_msg = "\nvalidate: {} {} {}({})".format(
                check_item,
                comparator,
                expect_value,
                type(expect_value).__name__
            )

            try:
                validator.to_value(self.session_context.test_variables_mapping)
                validator_dict["check_result"] = "pass"
                validate_msg += "\t==> pass"
                logger.log_debug(validate_msg)
            except (AssertionError, TypeError):
                validate_pass = False
                validator_dict["check_result"] = "fail"
                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)
                failures.append(validate_msg)

            self.validation_results["validate_extractor"].append(validator_dict)

            # restore validator args, in test_case of running multiple times
            validator.update_args(validator_args)

        if not validate_pass:
            failures_string = "\n".join([failure for failure in failures])
            raise exceptions.ValidationFailure(failures_string)
コード例 #29
0
def unset_os_environ(variables_mapping):
    """ set variables mapping to os.environ
    """
    for variable in variables_mapping:
        os.environ.pop(variable)
        logger.log_debug("Unset OS environment variable: {}".format(variable))
コード例 #30
0
    def _run_test(self, test_dict):
        """ run single teststep.

        Args:
            test_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"
                        },
                        "json": {"name": "user", "password": "******"}
                    },
                    "extract": {},              # optional
                    "validate": [],             # optional
                    "setup_hooks": [],          # optional
                    "teardown_hooks": []        # optional
                }

        Raises:
            exceptions.ParamsError
            exceptions.ValidationFailure
            exceptions.ExtractFailure

        """
        # clear meta data first to ensure independence for each test
        self.__clear_test_data()

        # check skip
        self._handle_skip_feature(test_dict)

        # prepare
        test_dict = utils.lower_test_dict_keys(test_dict)
        test_variables = test_dict.get("variables", {})
        self.session_context.init_test_variables(test_variables)

        # teststep name
        test_name = test_dict.get("name", "")

        # parse test request
        raw_request = test_dict.get('request', {})
        parsed_test_request = self.session_context.eval_content(raw_request)
        self.session_context.update_test_variables("request",
                                                   parsed_test_request)

        # setup hooks
        setup_hooks = test_dict.get("setup_hooks", [])
        if setup_hooks:
            self.do_hook_actions(setup_hooks, "setup")

        try:
            url = parsed_test_request.pop('url')
            method = parsed_test_request.pop('method')
            parsed_test_request.setdefault("verify", self.verify)
            group_name = parsed_test_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_test_request))

        # request
        resp = self.http_client_session.request(method,
                                                url,
                                                name=(group_name or test_name),
                                                **parsed_test_request)
        resp_obj = response.ResponseObject(resp)

        # teardown hooks
        teardown_hooks = test_dict.get("teardown_hooks", [])
        if teardown_hooks:
            self.session_context.update_test_variables("response", resp_obj)
            self.do_hook_actions(teardown_hooks, "teardown")

        # extract
        extractors = test_dict.get("extract", {})
        extracted_variables_mapping = resp_obj.extract_response(extractors)
        self.session_context.update_session_variables(
            extracted_variables_mapping)

        # validate
        validators = test_dict.get("validate", [])
        try:
            self.session_context.validate(validators, resp_obj)

        except (exceptions.ParamsError, exceptions.ValidationFailure,
                exceptions.ExtractFailure):
            err_msg = "{} DETAILED REQUEST & RESPONSE {}\n".format(
                "*" * 32, "*" * 32)

            # log request
            err_msg += "====== request details ======\n"
            err_msg += "url: {}\n".format(url)
            err_msg += "method: {}\n".format(method)
            err_msg += "headers: {}\n".format(
                parsed_test_request.pop("headers", {}))
            for k, v in parsed_test_request.items():
                v = utils.omit_long_data(v)
                err_msg += "{}: {}\n".format(k, repr(v))

            err_msg += "\n"

            # log response
            err_msg += "====== response details ======\n"
            err_msg += "status_code: {}\n".format(resp_obj.status_code)
            err_msg += "headers: {}\n".format(resp_obj.headers)
            err_msg += "body: {}\n".format(repr(resp_obj.text))
            logger.log_error(err_msg)

            raise

        finally:
            self.validation_results = self.session_context.validation_results