You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 10:30:13