HttpRunner源码分析(数据驱动csv文件只取第一行数据问题)

**由于做数据驱动时,变量获取数据总是csv里的第一行数据,因此看了一下源码,但是还是很蒙b**

一、运行逻辑步骤

1.生成的pytest用例文件中:
a.测试类类继承HttpRunner;
b.入口为test_start(),如果需要进行数据驱动,则重写test_start()方法改为传参的方式test_start(param)
在这里插入图片描述
源码的test_start()方法:

    def test_start(self, param: Dict = None) -> "HttpRunner":
        """main entrance, discovered by pytest"""
        self.__init_tests__()  # 1.将测试用例中的测试步骤都加入到列表
        self.__project_meta = self.__project_meta or load_project_meta(
            self.__config.path  # 2.加载元数据信息,load testcases, .env, debugtalk.py
        )
        self.__case_id = self.__case_id or str(uuid.uuid4()) # 生成case_id
        st_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())  # 日志保存时间(这我自己加的)
        self.__log_path = self.__log_path or os.path.join(   # 日志目录
            self.__project_meta.RootDir, "logs", f"{st_time}-{self.__case_id}.run.log"
        )
        log_handler = logger.add(self.__log_path, level="DEBUG")  # 3.生成日志

        # parse config name
        config_variables = self.__config.variables  # 4.config-获取变量信息,默认是{}
        if param:
            config_variables.update(param)  # 5.传入参数中的变量信息更新到config_variables
        config_variables.update(self.__session_variables)  # 别的地方设置的变量也加进来
        self.__config.name = parse_data(  # 6.获取config name
            self.__config.name, config_variables, self.__project_meta.functions
        )

        if USE_ALLURE:  # allure报告
            # update allure report meta
            allure.dynamic.title(self.__config.name)
            allure.dynamic.description(f"TestCase ID: {self.__case_id}")

        logger.info(
            f"Start to run testcase: {self.__config.name}, TestCase ID: {self.__case_id}"
        )

        # 以上准备步骤完成,开始跑用例
        try:
            return self.run_testcase(
                TestCase(config=self.__config, teststeps=self.__teststeps)
            )
        finally:
            logger.remove(log_handler)
            logger.info(f"generate testcase log: {self.__log_path}")

源码的run_testcase()方法:

    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  # 获取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
            )

            # 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
            extracted_variables.update(extract_mapping)

        self.__session_variables.update(extracted_variables)
        self.__duration = time.time() - self.__start_at
        return self

二、其他处理模块

数据处理之类的方法都放在parser.py中,下面是举例

数据关联 变 量 、 {变量}、 变量的代逻辑源码示例:
在这里插入图片描述
在这里插入图片描述

def parse_string(
    raw_string: Text,
    variables_mapping: VariablesMapping,
    functions_mapping: FunctionsMapping,
) -> Any:
    """ parse string content with variables and functions mapping.

    Args:
        raw_string: raw string content to be parsed.
        variables_mapping: variables mapping.
        functions_mapping: functions mapping.

    Returns:
        str: parsed string content.

    Examples:
        >>> raw_string = "abc${add_one($num)}def"
        >>> variables_mapping = {"num": 3}
        >>> functions_mapping = {"add_one": lambda x: x + 1}
        >>> parse_string(raw_string, variables_mapping, functions_mapping)
            "abc4def"

    """
    try:
        match_start_position = raw_string.index("$", 0)  # 3
        parsed_string = raw_string[0:match_start_position]  # abc
    except ValueError:
        parsed_string = raw_string
        return parsed_string

    while match_start_position < len(raw_string):

        # Notice: notation priority
        # $$ > ${func($a, $b)} > $var

        # search $$
        dollar_match = dolloar_regex_compile.match(raw_string, match_start_position)
        if dollar_match:
            match_start_position = dollar_match.end()
            parsed_string += "$"
            continue

        # search function like ${func($a, $b)}
        func_match = function_regex_compile.match(raw_string, match_start_position)
        if func_match:
            func_name = func_match.group(1)
            func = get_mapping_function(func_name, functions_mapping)

            func_params_str = func_match.group(2)
            function_meta = parse_function_params(func_params_str)
            args = function_meta["args"]
            kwargs = function_meta["kwargs"]
            parsed_args = parse_data(args, variables_mapping, functions_mapping)
            parsed_kwargs = parse_data(kwargs, variables_mapping, functions_mapping)

            try:
                func_eval_value = func(*parsed_args, **parsed_kwargs)
            except Exception as ex:
                logger.error(
                    f"call function error:\n"
                    f"func_name: {func_name}\n"
                    f"args: {parsed_args}\n"
                    f"kwargs: {parsed_kwargs}\n"
                    f"{type(ex).__name__}: {ex}"
                )
                raise

            func_raw_str = "${" + func_name + f"({func_params_str})" + "}"
            if func_raw_str == raw_string:
                # raw_string is a function, e.g. "${add_one(3)}", return its eval value directly
                return func_eval_value

            # raw_string contains one or many functions, e.g. "abc${add_one(3)}def"
            parsed_string += str(func_eval_value)
            match_start_position = func_match.end()
            continue

        # search variable like ${var} or $var
        var_match = variable_regex_compile.match(raw_string, match_start_position)
        if var_match:
            var_name = var_match.group(1) or var_match.group(2)
            var_value = get_mapping_variable(var_name, variables_mapping)

            if f"${var_name}" == raw_string or "${" + var_name + "}" == raw_string:
                # raw_string is a variable, $var or ${var}, return its value directly
                return var_value

            # raw_string contains one or many variables, e.g. "abc${var}def"
            parsed_string += str(var_value)
            match_start_position = var_match.end()
            continue

        curr_position = match_start_position
        try:
            # find next $ location
            match_start_position = raw_string.index("$", curr_position + 1)
            remain_string = raw_string[curr_position:match_start_position]
        except ValueError:
            remain_string = raw_string[curr_position:]
            # break while loop
            match_start_position = len(raw_string)

        parsed_string += remain_string

    return parsed_string

数据驱动3种方式源码示例:
在这里插入图片描述

三、求助大佬们!!

感觉hrun数据驱动运行时,有bug,所以看了下源码,但是还很蒙b,bug如下:
(我操作应该是没问题的啊!)
csv数据文件:
在这里插入图片描述
生成的pytest用例代码:
在这里插入图片描述
点击运行会运行2遍:
第一遍:
在这里插入图片描述
第二遍:
在这里插入图片描述
尝试过很多次,还是这样,这是为啥呢?求助大佬

四、问题解决

感谢大佬,问题得以解决!
pydantic包的版本问题导致,具体原因未知,但好歹解决
安装pydantic包:pip install pydantic==1.8.2

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值