httprunner3-用例执行源码走读

test_start()入口
TestCaseUserLogin().test_start()
用pytest会走这个方法,return是一个HttpRunner的对象

    def test_start(self, param: Dict = None) -> "HttpRunner":
        """main entrance, discovered by pytest"""
        self.__init_tests__()
        self.__project_meta = self.__project_meta or load_project_meta(
            self.__config.path
        )
        self.__case_id = self.__case_id or str(uuid.uuid4())
        self.__log_path = self.__log_path or os.path.join(
            self.__project_meta.RootDir, "logs", f"{self.__case_id}.run.log"
        )
        log_handler = logger.add(self.__log_path, level="INFO")

        # parse config name
        config_variables = self.__config.variables
        if param:
            config_variables.update(param)
        config_variables.update(self.__session_variables)
        self.__config.name = parse_data(
            self.__config.name, config_variables, self.__project_meta.functions
        )

        if USE_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}")

test_start方法可接受参数化(能使用pytest的方法进行数据驱动),入参数据为dict类型

self.init_tests():获取配置信息,将配置信息拼接到 teststeps,完成解析组装
self.__project_meta0. 加载测试用例、.env、debugtalk.py函数
self.__case_id 生成caseid,之前已经赋值就不变
self.__config.variables():去获取配置信息的变量
执行用例入口
self.run_testcase(
TestCase(config=self.__config, teststeps=self.__teststeps)
)

def run_testcase
遍历测试步骤,通过self.__run_step执行步骤
在这里插入图片描述

def __run_step(self, step: TStep) -> Dict:
    """run teststep, teststep maybe a request or referenced testcase"""
    '''step对象是request还是调用testcase'''
    logger.info(f"run step begin: {step.name} >>>>>>")
    if step.request:
        '''如果是直接请求接口就用__run_step_request方法'''
        step_data = self.__run_step_request(step)
        logger.info(f"step_data={step_data}")
    elif step.testcase:
        '''如果是调用其他case就用__run_step_testcase方法,递归写法,直接调用回run方法->run_path->run_testcase'''
        step_data = self.__run_step_testcase(step)
    else:
        raise ParamsError(
            f"teststep is neither a request nor a referenced testcase: {step.dict()}"
        )

    self.__step_datas.append(step_data)
    logger.info(f"run step end: {step.name} <<<<<<\n")
    return step_data.export_vars

run_step_testcase方法

def __run_step_testcase(self, step: TStep) -> StepData:
    """run teststep: referenced testcase"""
    step_data = StepData(name=step.name)
    step_variables = step.variables
    step_export = step.export

    # setup hooks
    if step.setup_hooks:
        self.__call_hooks(step.setup_hooks, step_variables, "setup testcase")

    if hasattr(step.testcase, "config") and hasattr(step.testcase, "teststeps"):
        testcase_cls = step.testcase  
        case_result = (
            testcase_cls()
            .with_session(self.__session)
            .with_case_id(self.__case_id)
            .with_variables(step_variables)
            .with_export(step_export)
            .run()
            '''直接调用testcase调用run()方法执行用例,run()会再递归走到run_testcase'''
        )

def __run_step_request

n_step_requedef __run_step_requestst(self, step: TStep) -> StepData:
    """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
    )'''解析参数组装数据,处理变量和debugtalk方法'''
    parsed_request_dict["headers"].setdefault(
        "HRUN-Request-ID",
        f"HRUN-{self.__case_id}-{str(int(time.time() * 1000))[-6:]}",
    )
    step.variables["request"] = parsed_request_dict

    # setup hooks
    if step.setup_hooks:
        self.__call_hooks(step.setup_hooks, step.variables, "setup request")

    # prepare arguments 
    '''request参数的赋值'''
    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["verify"] = self.__config.verify
    parsed_request_dict["json"] = parsed_request_dict.pop("req_json", {})

    # request 
    '''request请求主入口'''
    resp = self.__session.request(method, url, **parsed_request_dict)
    resp_obj = ResponseObject(resp)
    step.variables["response"] = resp_obj

    # teardown hooks
    if step.teardown_hooks:
        self.__call_hooks(step.teardown_hooks, step.variables, "teardown request")
    #打印请求日志
    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.variables, self.__project_meta.functions)
    step_data.export_vars = extract_mapping
    #将提取的参数更新到变量中
    variables_mapping = step.variables
    variables_mapping.update(extract_mapping)

    # validate 断言流程
    validators = step.validators
    session_success = False
    try:
        resp_obj.validate(
            validators, variables_mapping, self.__project_meta.functions
        )
        session_success = True
    except ValidationFailure:
        session_success = False
        log_req_resp_details()
        # log testcase duration before raise ValidationFailure
        self.__duration = time.time() - self.__start_at
        raise
    finally:
        self.success = session_success
        step_data.success = session_success
        #处理用例执行结果
        if hasattr(self.__session, "data"):
            # httprunner.client.HttpSession, not locust.clients.HttpSession
            # save request & response meta data
            self.__session.data.success = session_success
            self.__session.data.validators = resp_obj.validation_results

            # save step data
            step_data.data = self.__session.data

    return step_data
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值