如何使Allure报告中logging.error所在父步骤变红?求Python软检查方案
问题描述
我在使用Allure编写嵌套步骤的测试用例时,采用了收集错误后统一断言的方式:当子步骤出错时,通过logging.error在Allure报告中展示错误,此时子步骤会显示为红色,但它的父步骤(比如示例中的"checks"、"checks 2")并未同步变红。请问如何让这些父步骤也变为红色?同时想了解还有哪些软检查的实现方式?
测试代码如下:
import allure import logging class E: def __init__(self): self.errors = True def add(self, result): if not result: logging.error("error") self.errors = False def test_1(driver): e = E() with allure.step("open page"): driver.get("https://www.youtube.com/") with allure.step("checks"): with allure.step("check 1"): e.add("youtube" not in driver.current_url) with allure.step("checks 2"): e.add("youtube" in driver.current_url) assert e.errors
解决方案
一、让父步骤同步显示为红色(对应Java的Allure.LIFECYCLE.fire实现)
Python的Allure同样提供了生命周期API,可以直接操作步骤状态。你可以通过AllureLifecycle获取当前嵌套的步骤栈,当子步骤出错时,向上遍历父步骤并将其状态设置为失败。
修改后的代码示例:
import allure import logging from allure_commons.types import Status from allure_commons.lifecycle import AllureLifecycle def set_parent_steps_failed(): lifecycle = AllureLifecycle() # 获取当前活跃的步骤栈 steps = list(lifecycle._steps_stack) # 遍历父步骤(跳过当前子步骤),将未失败的父步骤置为失败 for step in reversed(steps[:-1]): lifecycle.update_step( step.uuid, lambda s: s.status == Status.FAILED or setattr(s, 'status', Status.FAILED) ) class E: def __init__(self): self.errors = True def add(self, result): if not result: logging.error("error") self.errors = False # 触发父步骤置为失败 set_parent_steps_failed() def test_1(driver): e = E() with allure.step("open page"): driver.get("https://www.youtube.com/") with allure.step("checks"): with allure.step("check 1"): e.add("youtube" not in driver.current_url) with allure.step("checks 2"): e.add("youtube" in driver.current_url) assert e.errors
核心逻辑:通过AllureLifecycle的_steps_stack获取当前所有嵌套的步骤实例,遍历并修改父步骤的状态为Status.FAILED,这样父步骤在报告中就会显示为红色。
二、其他软检查实现方式
1. 原生断言+错误收集+Allure动态标记
无需额外类,直接在步骤中捕获断言失败,记录错误后继续执行,最后统一校验:
import allure import logging def test_1(driver): errors = [] with allure.step("open page"): driver.get("https://www.youtube.com/") with allure.step("checks"): with allure.step("check 1"): try: assert "youtube" not in driver.current_url except AssertionError: err_msg = "Check 1失败:URL不应包含youtube" errors.append(err_msg) logging.error(err_msg) allure.attach(err_msg, name="错误详情", attachment_type=allure.attachment_type.TEXT) # 标记当前步骤失败 allure.dynamic.step_status(allure.Status.FAILED) with allure.step("checks 2"): try: assert "youtube" in driver.current_url except AssertionError: err_msg = "Check 2失败:URL应包含youtube" errors.append(err_msg) logging.error(err_msg) allure.attach(err_msg, name="错误详情", attachment_type=allure.attachment_type.TEXT) allure.dynamic.step_status(allure.Status.FAILED) assert len(errors) == 0, f"软检查失败,共{len(errors)}个错误:{' | '.join(errors)}"
2. 使用pytest-check插件(推荐)
这是pytest生态中专门用于软断言的插件,与Allure集成良好,无需手动维护错误状态:
- 安装:
pip install pytest-check - 使用示例:
import allure import pytest_check as check def test_1(driver): with allure.step("open page"): driver.get("https://www.youtube.com/") with allure.step("checks"): with allure.step("check 1"): check.is_not_in("youtube", driver.current_url, "Check 1失败:URL不应包含youtube") with allure.step("checks 2"): check.is_in("youtube", driver.current_url, "Check 2失败:URL应包含youtube") # 统一校验所有软断言结果 check.assert_no_failures()
该插件会自动收集所有断言失败,在测试结束后统一抛出异常,同时Allure报告中每个失败的检查步骤和其父步骤都会自动标记为红色。
3. 自定义装饰器封装软检查
可以封装装饰器,自动处理步骤中的断言失败并同步标记父步骤:
import allure import logging from functools import wraps from allure_commons.lifecycle import AllureLifecycle from allure_commons.types import Status def set_parent_steps_failed(): lifecycle = AllureLifecycle() steps = list(lifecycle._steps_stack) for step in reversed(steps[:-1]): lifecycle.update_step( step.uuid, lambda s: setattr(s, 'status', Status.FAILED) if s.status == Status.PASSED else None ) def soft_check(step_name): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): with allure.step(step_name) as step: try: func(*args, **kwargs) return True except AssertionError as e: err_msg = str(e) or f"{step_name}失败" logging.error(err_msg) allure.attach(err_msg, name="错误详情", attachment_type=allure.attachment_type.TEXT) step.status = Status.FAILED set_parent_steps_failed() return False return wrapper return decorator def test_1(driver): errors = [] with allure.step("open page"): driver.get("https://www.youtube.com/") @soft_check("check 1") def check_1(): assert "youtube" not in driver.current_url, "URL不应包含youtube" @soft_check("checks 2") def check_2(): assert "youtube" in driver.current_url, "URL应包含youtube" with allure.step("checks"): errors.append(check_1()) errors.append(check_2()) assert all(errors), "软检查存在失败项"
内容的提问来源于stack exchange,提问作者Antonio Gamornapory
相关产品推荐
相关产品推荐

