httprunner3 优化allure报告

httprunner3 优化allure报告



前言

httprunner自带allure报告,由于项目个性化需求,原生报告不足够满足实际需要,故对httprunner3源码进行allure注解的添加,让你的报告更加丰满。


一、原生报告

直接执行用例,生成allure报告。
注:网上已经很多文章说明如何生成allure报告,在此不在赘述。
在这里插入图片描述

二、用例中添加注解

httprunner添加allure方式同pytest,如@allure.feature(“testcase with feature”)。通过@pytest.mark.parametrize参数化的方式,httprunner自动将参数打印在报告中。

@allure.feature("testcase with feature")
class TestCaseDemoTestcaseRef(HttpRunner):
    @pytest.mark.parametrize(
        "param",
        Parameters(
            {"foo1": ["bar1", "bar2"]}
        ),
    )
    def test_start(self, param):
        super().test_start(param)
    config = (
        Config("request methods testcase: reference testcase")
        .variables(
            **{
                "foo1": "testsuite_config_bar1",
                "expect_foo1": "testsuite_config_bar1",
                "expect_foo2": "config_bar2",
            }
        )
        .base_url("https://postman-echo.com")
        .verify(False)
    )

    teststeps = [
        # allure.step("step 1"),
        Step(
            RunTestCase("request with functions")
            .with_variables(
                **{"foo1": "testcase_ref_bar1", "expect_foo1": "testcase_ref_bar1"}
            )
            .call(DemoTestcaseRequest)
            .export(*["foo3"])
        ),
        # allure.step("step 2"),
        Step(
            RunRequest("post form data")
            .with_variables(**{"foo1": "bar1"})
            .post("/post")
            .with_headers(
                **{
                    "User-Agent": "HttpRunner/${get_httprunner_version()}",
                    "Content-Type": "application/x-www-form-urlencoded",
                }
            )
            .with_data("foo1=$foo1&foo2=$foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.form.foo1", "$foo1")
            .assert_equal("body.form.foo2", "bar21")
        ),
    ]
  • 效果如下
    在这里插入图片描述

三、源码添加注解

1.添加日志

添加用例执行日志,仅包含该用例执行的日志。在runner.py的test_start方法中添加:

allure.attach.file(self.__log_path, name='log', attachment_type=allure.attachment_type.TEXT)
  • 代码如下
 # runner.py
 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="DEBUG")

        # 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:
            allure.attach.file(self.__log_path, name='log', attachment_type=allure.attachment_type.TEXT)
            logger.remove(log_handler)
            logger.info(f"generate testcase log: {self.__log_path}")
  • 实际效果:
    在这里插入图片描述

2.step展示请求数据

在每个step下打印请求与返回的数据。step为RunTestCase类型则遍历打印引用用例中的step。在runner.py的__run_step_request方法中添加:

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

allure_msg += "\n"

# log response
allure_msg += "======================== response details ========================\n"
allure_msg += f"➩status_code: {resp.status_code}\n\n"
allure_msg += f"➩headers: {resp.headers}\n\n"
allure_msg += f"➩body: {repr(resp.text)}\n\n"
allure.attach(allure_msg, name="requests info", attachment_type=allure.attachment_type.TEXT)

注:需要先定义allure_msg=’’,打印的据及展示方式根据实际需求设置。

  • 修改后的源码如下
# runner.py
def __run_step_request(self, step: TStep) -> StepData:
    """run teststep: request"""
    allure_msg = ''
    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
    )
    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
    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
    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_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

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

        allure_msg += "\n"

        # log response
        allure_msg += "======================== response details ========================\n"
        allure_msg += f"➩status_code: {resp.status_code}\n\n"
        allure_msg += f"➩headers: {resp.headers}\n\n"
        allure_msg += f"➩body: {repr(resp.text)}\n\n"
        allure.attach(allure_msg, name="requests info", attachment_type=allure.attachment_type.TEXT)

    return step_data
  • 效果如下
    在这里插入图片描述

总结

初学httprunner3,文章会持续更新,欢迎大神的指正和指导,感谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值