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

如何在自动化测试失败截图中标记失败区域(添加矩形框)

在Allure失败截图中添加标记/矩形框的解决方案

要给失败用例的截图添加标记(比如红色矩形框),核心是将浏览器截图转为可编辑图像,定位失败元素坐标后用绘图工具添加标记,再将修改后的图像附加到Allure报告中。以下是具体实现步骤:

1. 安装依赖

需要用到Pillow库处理图像,执行安装命令:

pip install pillow

2. 修改conftest.py中的截图逻辑

更新log_on_failure fixture,添加图像编辑和画框逻辑:

from PIL import Image, ImageDraw
import io
import time
import pytest
import allure
from allure_commons.types import AttachmentType
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium import webdriver

@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    rep = outcome.get_result()
    setattr(item, "rep_" + rep.when, rep)
    return rep

@pytest.fixture()
def log_on_failure(request, setup_teardown):
    yield
    item = request.node
    driver = setup_teardown
    if item.rep_call.failed:
        # 获取原始截图并转为PIL图像对象
        screenshot_bytes = driver.get_screenshot_as_png()
        img = Image.open(io.BytesIO(screenshot_bytes))
        draw = ImageDraw.Draw(img)

        # 检查是否有失败元素记录,有则画红色矩形框
        if hasattr(item, 'failed_element'):
            element = item.failed_element
            # 滚动到元素可见位置(避免元素在视口外)
            driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", element)
            # 获取元素坐标和尺寸
            rect = element.rect
            left, top = rect['x'], rect['y']
            right, bottom = left + rect['width'], top + rect['height']
            # 绘制红色矩形,线宽5
            draw.rectangle([left, top, right, bottom], outline='red', width=5)

        # 将修改后的图像转为字节流
        modified_screenshot = io.BytesIO()
        img.save(modified_screenshot, format='PNG')
        modified_screenshot.seek(0)

        # 附加到Allure报告
        allure.attach(
            modified_screenshot.getvalue(),
            name=f"Fail_{request.function.__name__}_{time.strftime('%y_%m_%d_%H_%M')}",
            attachment_type=AttachmentType.PNG
        )

@pytest.fixture(params=["chrome"], scope="function")
def setup_teardown(request):
    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
    driver.get(read_configuration("info", "site_url"))  # 假设read_configuration是你的配置读取方法
    request.cls.driver = driver
    driver.maximize_window()
    yield driver
    driver.quit()

3. 更新POM类的断言方法

在页面对象的断言方法中,当断言失败时将当前元素记录到测试节点:

from selenium.webdriver.common.by import By

class MainHeader:
    def __init__(self, driver):
        self.driver = driver
        # 定义页面元素定位(根据实际项目调整)
        self.website_icon = driver.find_element(By.CSS_SELECTOR, ".header-icon")
        self.chatai_btn = driver.find_element(By.ID, "chatai-link")
        self.search_field = driver.find_element(By.NAME, "search")
        self.search_btn = driver.find_element(By.CSS_SELECTOR, ".search-btn")
        self.login_btn = driver.find_element(By.CLASS_NAME, "login-btn")

    def website_icon_is_displayed(self, test_node):
        try:
            assert self.website_icon.is_displayed()
        except AssertionError:
            test_node.failed_element = self.website_icon
            raise

    def chatai_is_displayed(self, test_node):
        try:
            assert self.chatai_btn.is_displayed()
        except AssertionError:
            test_node.failed_element = self.chatai_btn
            raise

    def search_field_is_displayed(self, test_node):
        try:
            assert self.search_field.is_displayed()
        except AssertionError:
            test_node.failed_element = self.search_field
            raise

    def search_button_displayed_and_able(self, test_node):
        try:
            assert self.search_btn.is_displayed() and self.search_btn.is_enabled()
        except AssertionError:
            test_node.failed_element = self.search_btn
            raise

    def login_button_displayed_and_able(self, test_node):
        try:
            assert self.login_btn.is_displayed() and self.login_btn.is_enabled()
        except AssertionError:
            test_node.failed_element = self.login_btn
            raise

4. 修改测试类传递测试节点

在测试方法中传入request对象,传递给POM的断言方法:

import pytest
import allure
from allure_commons.types import Severity

@pytest.mark.usefixtures("setup_teardown", "log_on_failure")
class TestMain:
    @allure.severity(Severity.NORMAL)
    def test_check_header_items(self, request):
        main = MainHeader(self.driver)
        main.website_icon_is_displayed(request.node)
        main.chatai_is_displayed(request.node)
        main.search_field_is_displayed(request.node)
        main.search_button_displayed_and_able(request.node)
        main.login_button_displayed_and_able(request.node)

注意事项

  • 如果失败场景不是元素断言(比如页面跳转失败),则不会添加标记,直接使用原始截图
  • 使用element.rect获取坐标比location更准确,避免因浏览器视口差异导致的框位偏移
  • 截图前滚动元素到视口中心,确保标记区域在截图范围内

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 20:05:35