11.Allure与pytest集成

Allure 与 pytest 集成详解

1. 什么是 Allure 与 pytest 集成

1.1 集成概述

Allure 与 pytest 集成是指将 Allure 测试报告框架与 pytest 测试框架无缝结合,使 pytest 测试用例的执行结果能够自动生成美观、详细的 Allure 测试报告。这种集成让测试人员可以:

  • 自动生成企业级测试报告
  • 查看详细的测试步骤和附件
  • 追踪测试历史趋势
  • 方便地分享测试结果给团队

1.2 为什么需要集成

没有集成的情况

# 运行 pytest 测试
pytest test_example.py -v

# 输出结果
======================== test session starts ==========================
platform win32 -- Python 3.9.0, pytest-7.0.0
collected 3 items

test_example.py::test_login PASSED                                  [ 33%]
test_example.py::test_create_user PASSED                              [ 66%]
test_example.py::test_delete_user FAILED                              [100%]

======================== FAILURES ==========================
test_delete_user FAILED
AssertionError: assert False

问题

  • 报告不够直观,只是简单的文本输出
  • 无法看到详细的测试步骤
  • 无法附加截图、日志等附件
  • 无法查看历史趋势
  • 不适合向领导汇报

集成 Allure 后

# 运行测试并生成 Allure 报告
pytest test_example.py --alluredir=./allure-results
allure serve ./allure-results

# 生成一个美观的 HTML 报告,包含:
# - 详细的测试步骤
# - 测试截图
# - 测试日志
# - 历史趋势图
# - 测试分类和标签
# - 环境信息

优势

  • 美观的 HTML 报告,适合向领导汇报
  • 详细的测试步骤,方便定位问题
  • 支持截图、日志等附件
  • 历史趋势分析
  • 测试分类和标签管理
  • 团队协作友好

1.3 集成的工作原理

pytest 执行测试用例
    ↓
allure-pytest 插件收集测试结果
    ↓
生成 Allure 结果数据(JSON/XML 格式)
    ↓
保存到 allure-results 目录
    ↓
使用 Allure 命令行工具生成 HTML 报告
    ↓
在浏览器中查看美观的报告

1.4 集成的核心组件

  1. allure-pytest:pytest 的 Allure 适配器插件
  2. Allure 命令行工具:用于生成和查看报告
  3. pytest:测试框架本身
  4. 测试代码:使用 Allure 装饰器和 API 的测试用例

2. 环境准备和安装

2.1 系统要求

Python 版本

  • Python 3.6 或更高版本
  • 推荐使用 Python 3.8+

pytest 版本

  • pytest 5.0 或更高版本
  • 推荐使用 pytest 7.0+

操作系统

  • Windows
  • macOS
  • Linux

2.2 安装 pytest

基本安装

pip install pytest

指定版本安装

# 安装特定版本
pip install pytest==7.4.0

# 安装最新版本
pip install pytest --upgrade

验证安装

pytest --version

输出示例

pytest 7.4.0

2.3 安装 allure-pytest

基本安装

pip install allure-pytest

指定版本安装

# 安装特定版本
pip install allure-pytest==2.13.2

# 安装最新版本
pip install allure-pytest --upgrade

验证安装

pip show allure-pytest

输出示例

Name: allure-pytest
Version: 2.13.2
Summary: Allure pytest integration
Location: C:UsersAdministratorAppDataLocalProgramsPythonPython39libsite-packages

2.4 安装 Allure 命令行工具

2.4.1 Windows 安装

方法 1:使用 Scoop 安装(推荐)

# 1. 先安装 Scoop(如果没有)
# 在 PowerShell 中执行(以管理员身份)
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
irm get.scoop.sh | iex

# 2. 使用 Scoop 安装 Allure
scoop install allure

# 3. 验证安装
allure --version

方法 2:手动安装

# 1. 下载 Allure
# 访问 https://github.com/allure-framework/allure2/releases
# 下载 allure-2.13.2.zip(或最新版本)

# 2. 解压到某个目录,例如 C:allure

# 3. 将 C:allurebin 添加到系统环境变量 PATH 中
# 右键"此电脑" -> "属性" -> "高级系统设置" -> "环境变量"
# 在"系统变量"中找到 Path,点击"编辑",添加 C:allurebin

# 4. 验证安装
allure --version

2.4.2 macOS 安装

使用 Homebrew 安装

brew install allure

验证安装

allure --version

2.4.3 Linux 安装

Ubuntu/Debian

# 添加仓库
sudo apt-add-repository ppa:qameta/allure
sudo apt-get update

# 安装
sudo apt-get install allure

或者使用 Snap

sudo snap install allure --classic

验证安装

allure --version

2.5 验证完整安装

创建测试文件 test_installation.py

import pytest
import allure

def test_allure_installed():
    """验证 Allure 已正确安装"""
    assert True

def test_pytest_installed():
    """验证 pytest 已正确安装"""
    assert True

运行测试

pytest test_installation.py --alluredir=./allure-results -v

生成报告

allure serve ./allure-results

如果一切正常

  • pytest 会成功运行测试
  • 会在 ./allure-results 目录生成结果文件
  • allure serve 会在浏览器中打开报告

3. 基本集成步骤

3.1 最简单的集成示例

让我们从一个最简单的例子开始,了解如何将 Allure 与 pytest 集成。

步骤 1:创建测试文件

创建 test_simple.py

import pytest
import allure

def test_addition():
    """测试加法"""
    assert 1 + 1 == 2

def test_subtraction():
    """测试减法"""
    assert 5 - 3 == 2

def test_multiplication():
    """测试乘法"""
    assert 2 * 3 == 6

步骤 2:运行测试并生成 Allure 结果

pytest test_simple.py --alluredir=./allure-results

说明

  • --alluredir=./allure-results:指定 Allure 结果数据的保存目录
  • pytest 会自动使用 allure-pytest 插件收集测试结果
  • 结果数据会以 JSON 格式保存在 ./allure-results 目录中

步骤 3:生成并查看报告

allure serve ./allure-results

说明

  • allure serve 会:
    1. 生成临时的 HTML 报告
    2. 启动本地服务器
    3. 在默认浏览器中打开报告
  • 报告地址通常是 http://localhost:随机端口

步骤 4:查看报告

报告界面包含:

  • Overview(概览):测试执行的整体情况
  • Suites(套件):按测试套件查看
  • Graphs(图表):测试结果的可视化图表
  • Behaviors(行为):按行为分类查看
  • Packages(包):按包/模块查看

3.2 生成静态报告

除了使用 allure serve 查看临时报告,还可以生成静态 HTML 报告:

# 生成静态报告
allure generate ./allure-results -o ./allure-report --clean

# 查看报告(需要手动打开)
# Windows: 在文件管理器中打开 allure-report/index.html
# macOS: open allure-report/index.html
# Linux: xdg-open allure-report/index.html

参数说明

  • -o ./allure-report:指定报告输出目录
  • --clean:清理输出目录(如果已存在)

优势

  • 可以保存报告文件
  • 可以分享给其他人
  • 可以部署到 Web 服务器

3.3 完整的集成流程示例

项目结构

myproject/
├── tests/
│   ├── __init__.py
│   ├── test_login.py
│   └── test_user.py
├── allure-results/    # Allure 结果数据(自动生成)
├── allure-report/     # Allure 报告(自动生成)
└── pytest.ini         # pytest 配置文件

测试文件 tests/test_login.py

import pytest
import allure

@allure.feature("用户管理")
@allure.story("用户登录")
class TestLogin:
    """用户登录测试类"""

    @allure.title("测试管理员登录")
    def test_admin_login(self):
        """测试管理员登录"""
        with allure.step("打开登录页面"):
            print("打开登录页面")

        with allure.step("输入用户名和密码"):
            username = "admin"
            password = "admin123"
            print(f"输入用户名: {username}")
            print(f"输入密码: {password}")

        with allure.step("点击登录按钮"):
            print("点击登录按钮")

        with allure.step("验证登录成功"):
            assert True

    @allure.title("测试普通用户登录")
    def test_user_login(self):
        """测试普通用户登录"""
        with allure.step("打开登录页面"):
            print("打开登录页面")

        with allure.step("输入用户名和密码"):
            username = "user"
            password = "user123"
            print(f"输入用户名: {username}")
            print(f"输入密码: {password}")

        with allure.step("点击登录按钮"):
            print("点击登录按钮")

        with allure.step("验证登录成功"):
            assert True

运行测试

# 1. 运行测试并生成 Allure 结果
pytest tests/ --alluredir=./allure-results -v

# 2. 生成并查看报告
allure serve ./allure-results

4. pytest 配置文件集成

4.1 在 pytest.ini 中配置

创建 pytest.ini 文件

[pytest]
# 基本配置
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*

# Allure 配置
addopts = 
    --alluredir=./allure-results
    -v
    --tb=short

# 标记
markers =
    smoke: 冒烟测试
    regression: 回归测试
    slow: 慢速测试

使用配置

# 直接运行 pytest,会自动使用配置文件中的设置
pytest

# 不需要每次都指定 --alluredir
# pytest 会自动使用 pytest.ini 中的配置

4.2 在 pyproject.toml 中配置

创建 pyproject.toml 文件

[tool.pytest.ini_options]
# 基本配置
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]

# Allure 配置
addopts = [
    "--alluredir=./allure-results",
    "-v",
    "--tb=short"
]

# 标记
markers = [
    "smoke: 冒烟测试",
    "regression: 回归测试",
    "slow: 慢速测试"
]

使用配置

# 直接运行 pytest
pytest

4.3 在 conftest.py 中配置

创建 conftest.py 文件

import pytest
import allure
import os

def pytest_configure(config):
    """pytest 配置钩子"""
    # 确保 allure-results 目录存在
    allure_dir = "./allure-results"
    os.makedirs(allure_dir, exist_ok=True)

    # 配置 Allure 环境信息
    config.option.allure_report_dir = allure_dir

@pytest.fixture(scope="session", autouse=True)
def configure_allure_environment():
    """配置 Allure 环境信息"""
    environment = {
        "OS": os.name,
        "Python": os.sys.version,
        "Pytest": pytest.__version__,
    }

    # 写入环境信息到 allure-results 目录
    allure_dir = "./allure-results"
    os.makedirs(allure_dir, exist_ok=True)

    env_file = os.path.join(allure_dir, "environment.properties")
    with open(env_file, "w", encoding="utf-8") as f:
        for key, value in environment.items():
            f.write(f"{key}={value}n")

    yield

4.4 配置文件优先级

配置的优先级(从高到低):

  1. 命令行参数
  2. pytest.inipyproject.toml
  3. conftest.py 中的配置
  4. 默认配置

示例

# 命令行参数优先级最高
pytest --alluredir=./custom-results

# 即使 pytest.ini 中配置了 --alluredir=./allure-results
# 也会使用命令行中的 ./custom-results

5. Allure 装饰器与 pytest 集成

5.1 @allure.title – 设置测试标题

基本用法

import pytest
import allure

@allure.title("用户登录测试")
def test_login():
    """测试用户登录功能"""
    assert True

@allure.title("创建新用户")
def test_create_user():
    """测试创建用户功能"""
    assert True

动态标题

import pytest
import allure

@pytest.mark.parametrize("username", ["admin", "user", "guest"])
@allure.title("测试用户 {username} 的登录功能")
def test_login_with_username(username):
    """使用参数化测试,标题中包含参数"""
    assert username in ["admin", "user", "guest"]

在测试类中使用

import pytest
import allure

@allure.feature("用户管理")
class TestUser:
    """用户管理测试类"""

    @allure.title("测试管理员登录")
    def test_admin_login(self):
        """测试管理员登录"""
        assert True

    @allure.title("测试普通用户登录")
    def test_user_login(self):
        """测试普通用户登录"""
        assert True

5.2 @allure.description – 添加测试描述

使用装饰器参数

import allure

@allure.description("""
这是一个详细的测试描述:
1. 测试用户登录功能
2. 验证用户名和密码
3. 检查登录后的跳转
""")
def test_login():
    assert True

使用文档字符串

import allure

@allure.description
def test_login():
    """
    测试用户登录功能

    前置条件:
    - 用户已注册
    - 数据库连接正常

    测试步骤:
    1. 打开登录页面
    2. 输入用户名和密码
    3. 点击登录按钮

    预期结果:
    - 登录成功
    - 跳转到首页
    """
    assert True

5.3 @allure.step – 定义测试步骤

基本用法

import allure

@allure.step("打开登录页面")
def open_login_page():
    """打开登录页面"""
    print("正在打开登录页面...")
    return True

@allure.step("输入用户名: {username}")
def input_username(username):
    """输入用户名"""
    print(f"正在输入用户名: {username}")
    return True

@allure.step("点击登录按钮")
def click_login_button():
    """点击登录按钮"""
    print("正在点击登录按钮...")
    return True

def test_login():
    """测试登录流程"""
    open_login_page()
    input_username("admin")
    click_login_button()
    assert True

使用上下文管理器

import allure

def test_login_with_steps():
    """测试登录流程(在测试函数中定义步骤)"""

    with allure.step("打开登录页面"):
        print("打开登录页面")

    with allure.step("输入用户名和密码"):
        username = "admin"
        password = "123456"
        print(f"输入用户名: {username}")
        print(f"输入密码: {password}")

    with allure.step("点击登录按钮"):
        print("点击登录按钮")

    with allure.step("验证登录结果"):
        assert True

步骤嵌套

import allure

@allure.step("准备测试数据")
def prepare_data():
    """准备测试数据"""
    with allure.step("连接数据库"):
        print("连接数据库")

    with allure.step("查询用户信息"):
        print("查询用户信息")

    with allure.step("清理临时数据"):
        print("清理临时数据")

def test_with_nested_steps():
    """测试嵌套步骤"""
    prepare_data()
    assert True

5.4 @allure.severity – 设置测试优先级

优先级级别

import allure
import pytest

@allure.severity(allure.severity_level.BLOCKER)
def test_critical_login():
    """阻塞级别测试(最高优先级)"""
    assert True

@allure.severity(allure.severity_level.CRITICAL)
def test_important_feature():
    """严重级别测试"""
    assert True

@allure.severity(allure.severity_level.NORMAL)
def test_normal_feature():
    """正常级别测试(默认)"""
    assert True

@allure.severity(allure.severity_level.MINOR)
def test_minor_feature():
    """次要级别测试"""
    assert True

@allure.severity(allure.severity_level.TRIVIAL)
def test_trivial_feature():
    """轻微级别测试(最低优先级)"""
    assert True

按优先级运行测试

# 只运行阻塞级别的测试
pytest test_example.py --alluredir=./allure-results -s -v --allure-severities=blocker

# 运行阻塞和严重级别的测试
pytest test_example.py --alluredir=./allure-results -s -v --allure-severities=blocker,critical

5.5 @allure.feature 和 @allure.story – 功能分类

功能分类示例

import allure

@allure.feature("用户管理")
@allure.story("用户注册")
def test_user_register():
    """用户注册"""
    assert True

@allure.feature("用户管理")
@allure.story("用户登录")
def test_user_login():
    """用户登录"""
    assert True

@allure.feature("用户管理")
@allure.story("用户注销")
def test_user_logout():
    """用户注销"""
    assert True

@allure.feature("订单管理")
@allure.story("创建订单")
def test_create_order():
    """创建订单"""
    assert True

@allure.feature("订单管理")
@allure.story("查看订单")
def test_view_order():
    """查看订单"""
    assert True

按功能或故事运行测试

# 只运行"用户管理"功能的测试
pytest test_example.py --alluredir=./allure-results -s -v --allure-features="用户管理"

# 只运行"用户登录"故事的测试
pytest test_example.py --alluredir=./allure-results -s -v --allure-stories="用户登录"

5.6 组合使用多个装饰器

完整示例

import allure
import pytest

@allure.feature("用户管理")
@allure.story("用户登录")
@allure.severity(allure.severity_level.BLOCKER)
@allure.title("测试管理员登录功能")
@allure.description("""
测试管理员登录功能:
1. 输入正确的管理员账号和密码
2. 验证登录成功
3. 检查用户权限
""")
def test_admin_login():
    """测试管理员登录"""
    with allure.step("打开登录页面"):
        print("打开登录页面")

    with allure.step("输入管理员账号和密码"):
        username = "admin"
        password = "admin123"
        print(f"输入用户名: {username}")
        print(f"输入密码: {password}")

    with allure.step("点击登录按钮"):
        print("点击登录按钮")

    with allure.step("验证登录成功"):
        assert True

6. pytest fixture 与 Allure 集成

6.1 在 fixture 中使用 Allure 步骤

示例 1:基本 fixture

import pytest
import allure

@pytest.fixture
def login_user():
    """登录用户 fixture"""
    with allure.step("准备用户数据"):
        username = "admin"
        password = "admin123"

    with allure.step("执行登录操作"):
        print(f"用户 {username} 登录中...")
        # 模拟登录逻辑
        user_info = {"username": username, "logged_in": True}

    yield user_info

    with allure.step("清理登录状态"):
        print("清理登录状态")

def test_with_login_fixture(login_user):
    """使用登录 fixture 的测试"""
    assert login_user["logged_in"] is True
    assert login_user["username"] == "admin"

示例 2:带参数的 fixture

import pytest
import allure

@pytest.fixture
def user_data(request):
    """用户数据 fixture"""
    username = request.param

    with allure.step(f"准备用户 {username} 的数据"):
        user_info = {
            "username": username,
            "email": f"{username}@example.com",
            "role": "admin" if username == "admin" else "user"
        }

    return user_info

@pytest.mark.parametrize("user_data", ["admin", "user", "guest"], indirect=True)
def test_user_data(user_data):
    """测试用户数据"""
    assert user_data["username"] is not None
    assert "@example.com" in user_data["email"]

6.2 fixture 中的附件添加

示例:在 fixture 中添加截图

import pytest
import allure
from selenium import webdriver

@pytest.fixture(scope="function")
def driver():
    """WebDriver fixture"""
    driver = webdriver.Chrome()

    with allure.step("初始化浏览器"):
        driver.maximize_window()
        screenshot = driver.get_screenshot_as_png()
        allure.attach(
            screenshot,
            name="浏览器初始化",
            attachment_type=allure.attachment_type.PNG
        )

    yield driver

    # 如果测试失败,自动截图
    if hasattr(pytest, 'current_test_failed') and pytest.current_test_failed:
        with allure.step("测试失败,保存截图"):
            screenshot = driver.get_screenshot_as_png()
            allure.attach(
                screenshot,
                name="失败截图",
                attachment_type=allure.attachment_type.PNG
            )

    driver.quit()

def test_login_with_driver(driver):
    """使用 driver fixture 的测试"""
    driver.get("https://example.com/login")
    assert "login" in driver.current_url.lower()

6.3 使用 pytest hooks 集成 Allure

在 conftest.py 中使用 hooks

import pytest
import allure

def pytest_runtest_setup(item):
    """测试设置钩子"""
    # 在测试开始前执行
    allure.dynamic.description_html("""
    <h3>测试开始</h3>
    <p>这是动态添加的 HTML 描述</p>
    """)

def pytest_runtest_teardown(item, nextitem):
    """测试清理钩子"""
    # 在测试结束后执行
    pass

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """生成测试报告钩子"""
    outcome = yield
    rep = outcome.get_result()

    # 如果测试失败,添加附件
    if rep.when == "call" and rep.failed:
        allure.attach(
            f"测试失败: {rep.longrepr}",
            name="失败信息",
            attachment_type=allure.attachment_type.TEXT
        )

7. pytest 参数化与 Allure 集成

7.1 基本参数化

示例 1:简单参数化

import pytest
import allure

@pytest.mark.parametrize("a, b, expected", [
    (1, 2, 3),
    (3, 4, 7),
    (5, 6, 11),
])
@allure.title("测试加法: {a} + {b} = {expected}")
def test_addition(a, b, expected):
    """参数化测试加法"""
    with allure.step(f"计算 {a} + {b}"):
        result = a + b
        allure.attach(
            f"输入: a={a}, b={b}n结果: {result}n预期: {expected}",
            name="计算过程",
            attachment_type=allure.attachment_type.TEXT
        )

    assert result == expected

示例 2:参数化与 feature/story 结合

import pytest
import allure

@allure.feature("用户管理")
@allure.story("用户登录")
@pytest.mark.parametrize("username, password, expected", [
    ("admin", "admin123", True),
    ("user", "user123", True),
    ("guest", "wrong", False),
])
@allure.title("测试用户 {username} 登录")
def test_login_parametrize(username, password, expected):
    """参数化测试登录"""
    with allure.step(f"尝试使用 {username} 登录"):
        # 模拟登录逻辑
        login_success = (username, password) in [
            ("admin", "admin123"),
            ("user", "user123")
        ]

        allure.attach(
            f"用户名: {username}n密码: {password}n登录成功: {login_success}",
            name="登录信息",
            attachment_type=allure.attachment_type.TEXT
        )

        assert login_success == expected

7.2 参数化与动态标题

示例

import pytest
import allure

@pytest.mark.parametrize("user_type", ["admin", "user", "guest"])
def test_user_permissions(user_type):
    """测试不同用户类型的权限"""
    # 动态设置标题
    allure.dynamic.title(f"测试 {user_type} 用户权限")

    # 动态设置描述
    allure.dynamic.description(f"""
    测试 {user_type} 用户的权限:
    1. 检查登录权限
    2. 检查操作权限
    3. 检查数据访问权限
    """)

    with allure.step(f"检查 {user_type} 用户权限"):
        if user_type == "admin":
            permissions = ["read", "write", "delete"]
        elif user_type == "user":
            permissions = ["read", "write"]
        else:
            permissions = ["read"]

        allure.attach(
            f"用户类型: {user_type}n权限列表: {permissions}",
            name="权限信息",
            attachment_type=allure.attachment_type.TEXT
        )

        assert len(permissions) > 0

7.3 参数化与 fixture 结合

示例

import pytest
import allure

@pytest.fixture
def user_data(request):
    """用户数据 fixture"""
    user_type = request.param
    return {
        "admin": {"username": "admin", "role": "administrator"},
        "user": {"username": "user", "role": "normal"},
        "guest": {"username": "guest", "role": "visitor"}
    }[user_type]

@allure.feature("用户管理")
@pytest.mark.parametrize("user_data", ["admin", "user", "guest"], indirect=True)
@allure.title("测试 {user_data[username]} 用户功能")
def test_user_functionality(user_data):
    """测试不同用户的功能"""
    with allure.step(f"验证 {user_data['username']} 用户"):
        allure.attach(
            f"用户名: {user_data['username']}n角色: {user_data['role']}",
            name="用户信息",
            attachment_type=allure.attachment_type.TEXT
        )

        assert user_data["username"] is not None
        assert user_data["role"] is not None

8. pytest mark 与 Allure 集成

8.1 自定义 mark 与 Allure 标签

在 pytest.ini 中定义 mark

[pytest]
markers =
    smoke: 冒烟测试
    regression: 回归测试
    slow: 慢速测试
    api: API 测试
    ui: UI 测试

在测试中使用 mark

import pytest
import allure

@allure.feature("用户管理")
@pytest.mark.smoke
@allure.tag("冒烟测试")
def test_login_smoke():
    """冒烟测试:登录功能"""
    assert True

@allure.feature("用户管理")
@pytest.mark.regression
@allure.tag("回归测试")
def test_login_regression():
    """回归测试:登录功能"""
    assert True

@allure.feature("订单管理")
@pytest.mark.slow
@allure.tag("慢速测试")
def test_order_slow():
    """慢速测试:订单功能"""
    assert True

按 mark 运行测试

# 只运行冒烟测试
pytest -m smoke --alluredir=./allure-results

# 运行回归测试
pytest -m regression --alluredir=./allure-results

# 排除慢速测试
pytest -m "not slow" --alluredir=./allure-results

8.2 skip 和 xfail 与 Allure

skip 示例

import pytest
import allure

@allure.feature("用户管理")
@pytest.mark.skip(reason="功能尚未实现")
def test_unimplemented_feature():
    """跳过的测试"""
    assert False

@allure.feature("用户管理")
@pytest.mark.skipif(True, reason="条件不满足")
def test_conditional_skip():
    """条件跳过的测试"""
    assert True

xfail 示例

import pytest
import allure

@allure.feature("用户管理")
@pytest.mark.xfail(reason="已知问题,待修复")
def test_known_issue():
    """预期失败的测试"""
    assert False  # 这个失败是预期的

9. 附件功能集成

9.1 添加截图

Selenium 截图示例

import allure
import pytest
from selenium import webdriver

@pytest.fixture(scope="function")
def driver():
    """WebDriver fixture"""
    driver = webdriver.Chrome()
    yield driver
    driver.quit()

def test_login_with_screenshot(driver):
    """登录测试带截图"""
    driver.get("https://example.com/login")

    # 登录前截图
    screenshot = driver.get_screenshot_as_png()
    allure.attach(
        screenshot,
        name="登录页面",
        attachment_type=allure.attachment_type.PNG
    )

    # 执行登录操作
    driver.find_element_by_id("username").send_keys("admin")
    driver.find_element_by_id("password").send_keys("123456")
    driver.find_element_by_id("login-btn").click()

    # 登录后截图
    screenshot_after = driver.get_screenshot_as_png()
    allure.attach(
        screenshot_after,
        name="登录后页面",
        attachment_type=allure.attachment_type.PNG
    )

    assert "dashboard" in driver.current_url

9.2 添加文本和 JSON 附件

示例

import allure
import json

def test_with_attachments():
    """测试带多种附件"""
    # 添加普通文本
    allure.attach(
        "这是测试日志信息n包含多行内容",
        name="测试日志",
        attachment_type=allure.attachment_type.TEXT
    )

    # 添加 JSON 数据
    data = {
        "username": "admin",
        "status": "active",
        "permissions": ["read", "write", "delete"]
    }
    allure.attach(
        json.dumps(data, indent=2, ensure_ascii=False),
        name="用户数据",
        attachment_type=allure.attachment_type.JSON
    )

    # 添加 HTML
    html_content = """
    <html>
        <head><title>测试页面</title></head>
        <body>
            <h1>这是测试 HTML 内容</h1>
            <table border="1">
                <tr><th>用户名</th><th>状态</th></tr>
                <tr><td>admin</td><td>active</td></tr>
            </table>
        </body>
    </html>
    """
    allure.attach(
        html_content,
        name="测试页面",
        attachment_type=allure.attachment_type.HTML
    )

    assert True

9.3 在 fixture 中自动添加附件

示例

import pytest
import allure

@pytest.fixture(autouse=True)
def auto_attach_environment():
    """自动附加环境信息"""
    import platform
    import sys

    env_info = f"""
    操作系统: {platform.system()}
    系统版本: {platform.version()}
    Python 版本: {sys.version}
    """

    allure.attach(
        env_info,
        name="环境信息",
        attachment_type=allure.attachment_type.TEXT
    )

    yield

def test_with_auto_attach():
    """测试会自动附加环境信息"""
    assert True

10. 环境信息配置

10.1 创建环境配置文件

创建 environment.properties 文件

# 操作系统信息
OS=Windows 10
OS.Version=10.0.19045

# Python 信息
Python.Version=3.9.0
Pytest.Version=7.0.0

# 浏览器信息
Browser=Chrome
Browser.Version=91.0

# 测试环境
Environment=Test
Base.URL=https://test.example.com

# 数据库信息
Database=MySQL
Database.Version=8.0

10.2 使用环境配置文件

方法 1:手动复制

# 运行测试
pytest test_example.py --alluredir=./allure-results

# 手动复制 environment.properties 到 allure-results 目录
copy environment.properties allure-results/

# 生成报告
allure generate ./allure-results -o ./allure-report --clean

方法 2:使用 pytest fixture 自动生成

import pytest
import allure
import platform
import sys
import os

@pytest.fixture(scope="session", autouse=True)
def configure_allure_environment():
    """配置 Allure 环境信息"""
    environment = {
        "OS": platform.system(),
        "OS Version": platform.version(),
        "Python Version": sys.version.split()[0],
        "Pytest Version": pytest.__version__,
    }

    # 写入环境信息到 allure-results 目录
    allure_dir = "./allure-results"
    os.makedirs(allure_dir, exist_ok=True)

    env_file = os.path.join(allure_dir, "environment.properties")
    with open(env_file, "w", encoding="utf-8") as f:
        for key, value in environment.items():
            f.write(f"{key}={value}n")

    yield

10.3 动态添加环境信息

示例

import allure
import platform
import sys

def test_with_environment_info():
    """测试带环境信息"""
    # 动态添加环境信息
    allure.dynamic.environment(
        OS=platform.system(),
        Python=sys.version.split()[0],
        Pytest=pytest.__version__
    )

    assert True

11. 历史记录功能

11.1 启用历史记录

方法 1:保留历史目录

# 第一次运行
pytest test_example.py --alluredir=./allure-results
allure generate ./allure-results -o ./allure-report --clean

# 第二次运行(历史记录会自动保存)
pytest test_example.py --alluredir=./allure-results
allure generate ./allure-results -o ./allure-report
# 注意:不要使用 --clean,这样会保留历史记录

方法 2:手动复制历史目录

# 第一次运行
pytest test_example.py --alluredir=./allure-results
allure generate ./allure-results -o ./allure-report --clean

# 第二次运行前,复制历史目录
# Windows
xcopy /E /I allure-reporthistory allure-resultshistory

# Linux/Mac
cp -r allure-report/history allure-results/history

# 然后生成报告
pytest test_example.py --alluredir=./allure-results
allure generate ./allure-results -o ./allure-report

11.2 查看历史趋势

在 Allure 报告的”Graphs”页面,可以查看:

  • 测试用例数量的趋势
  • 通过率趋势
  • 失败率趋势
  • 执行时间趋势

12. 完整实战示例

12.1 示例 1:用户登录测试完整集成

项目结构

login_test_project/
├── tests/
│   ├── __init__.py
│   ├── conftest.py
│   └── test_login.py
├── pytest.ini
├── requirements.txt
└── README.md

conftest.py

import pytest
import allure
import platform
import sys
import os

@pytest.fixture(scope="session", autouse=True)
def configure_allure_environment():
    """配置 Allure 环境信息"""
    environment = {
        "OS": platform.system(),
        "OS Version": platform.version(),
        "Python Version": sys.version.split()[0],
        "Pytest Version": pytest.__version__,
    }

    allure_dir = "./allure-results"
    os.makedirs(allure_dir, exist_ok=True)

    env_file = os.path.join(allure_dir, "environment.properties")
    with open(env_file, "w", encoding="utf-8") as f:
        for key, value in environment.items():
            f.write(f"{key}={value}n")

    yield

@pytest.fixture(scope="function")
def login_data():
    """登录数据 fixture"""
    return {
        "admin": {"username": "admin", "password": "admin123"},
        "user": {"username": "user", "password": "user123"},
    }

test_login.py

import pytest
import allure

@allure.feature("用户管理")
@allure.story("用户登录")
class TestLogin:
    """用户登录测试类"""

    @allure.title("测试管理员登录")
    @allure.severity(allure.severity_level.BLOCKER)
    @allure.description("""
    测试管理员登录功能:
    1. 打开登录页面
    2. 输入管理员账号和密码
    3. 点击登录按钮
    4. 验证登录成功
    """)
    def test_admin_login(self, login_data):
        """测试管理员登录"""
        admin_data = login_data["admin"]

        with allure.step("打开登录页面"):
            print("打开登录页面")
            allure.attach(
                "登录页面已打开",
                name="页面状态",
                attachment_type=allure.attachment_type.TEXT
            )

        with allure.step("输入管理员账号和密码"):
            username = admin_data["username"]
            password = admin_data["password"]
            print(f"输入用户名: {username}")
            print(f"输入密码: {password}")

            allure.attach(
                f"用户名: {username}n密码: {password}",
                name="登录信息",
                attachment_type=allure.attachment_type.TEXT
            )

        with allure.step("点击登录按钮"):
            print("点击登录按钮")

        with allure.step("验证登录成功"):
            # 模拟登录验证
            login_success = True
            allure.attach(
                f"登录结果: {'成功' if login_success else '失败'}",
                name="登录结果",
                attachment_type=allure.attachment_type.TEXT
            )
            assert login_success

    @allure.title("测试普通用户登录")
    @allure.severity(allure.severity_level.NORMAL)
    def test_user_login(self, login_data):
        """测试普通用户登录"""
        user_data = login_data["user"]

        with allure.step("打开登录页面"):
            print("打开登录页面")

        with allure.step("输入普通用户账号和密码"):
            username = user_data["username"]
            password = user_data["password"]
            print(f"输入用户名: {username}")
            print(f"输入密码: {password}")

        with allure.step("点击登录按钮"):
            print("点击登录按钮")

        with allure.step("验证登录成功"):
            login_success = True
            assert login_success

    @allure.title("测试登录失败(错误密码)")
    @allure.severity(allure.severity_level.NORMAL)
    def test_login_failed(self, login_data):
        """测试登录失败"""
        with allure.step("打开登录页面"):
            print("打开登录页面")

        with allure.step("输入错误的密码"):
            username = login_data["admin"]["username"]
            password = "wrong_password"
            print(f"输入用户名: {username}")
            print(f"输入错误密码: {password}")

        with allure.step("点击登录按钮"):
            print("点击登录按钮")

        with allure.step("验证登录失败提示"):
            error_msg = "密码错误"
            allure.attach(
                f"错误信息: {error_msg}",
                name="错误信息",
                attachment_type=allure.attachment_type.TEXT
            )
            assert "密码错误" in error_msg

pytest.ini

[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*

addopts = 
    --alluredir=./allure-results
    -v
    --tb=short

markers =
    smoke: 冒烟测试
    regression: 回归测试

requirements.txt

pytest>=7.0.0
allure-pytest>=2.13.0

运行测试

# 安装依赖
pip install -r requirements.txt

# 运行测试
pytest

# 生成并查看报告
allure serve ./allure-results

12.2 示例 2:API 测试完整集成

test_api.py

import allure
import pytest
import requests
import json

@allure.feature("API 测试")
class TestAPI:
    """API 测试类"""

    BASE_URL = "https://api.example.com"

    @allure.story("用户 API")
    @allure.title("测试获取用户信息")
    @allure.severity(allure.severity_level.CRITICAL)
    def test_get_user_info(self):
        """测试获取用户信息"""
        user_id = 1

        with allure.step(f"发送 GET 请求获取用户 {user_id} 的信息"):
            url = f"{self.BASE_URL}/users/{user_id}"
            response = requests.get(url)

            allure.attach(
                f"请求 URL: {url}n请求方法: GET",
                name="请求信息",
                attachment_type=allure.attachment_type.TEXT
            )

        with allure.step("验证响应状态码"):
            assert response.status_code == 200
            allure.attach(
                f"状态码: {response.status_code}",
                name="响应状态码",
                attachment_type=allure.attachment_type.TEXT
            )

        with allure.step("验证响应数据"):
            data = response.json()
            allure.attach(
                json.dumps(data, indent=2, ensure_ascii=False),
                name="响应数据",
                attachment_type=allure.attachment_type.JSON
            )
            assert data["id"] == user_id
            assert "name" in data

    @allure.story("用户 API")
    @allure.title("测试创建用户")
    @allure.severity(allure.severity_level.CRITICAL)
    def test_create_user(self):
        """测试创建用户"""
        new_user = {
            "name": "测试用户",
            "email": "test@example.com",
            "age": 25
        }

        with allure.step("发送 POST 请求创建用户"):
            url = f"{self.BASE_URL}/users"
            response = requests.post(url, json=new_user)

            allure.attach(
                json.dumps(new_user, indent=2, ensure_ascii=False),
                name="请求数据",
                attachment_type=allure.attachment_type.JSON
            )

        with allure.step("验证创建成功"):
            assert response.status_code == 201
            data = response.json()
            allure.attach(
                json.dumps(data, indent=2, ensure_ascii=False),
                name="响应数据",
                attachment_type=allure.attachment_type.JSON
            )
            assert data["name"] == new_user["name"]
            assert "id" in data

13. CI/CD 集成

13.1 GitHub Actions 集成

创建 .github/workflows/test.yml

name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: '3.9'

      - name: Install dependencies
        run: |
          pip install -r requirements.txt

      - name: Install Allure
        run: |
          wget https://github.com/allure-framework/allure2/releases/download/2.13.2/allure-2.13.2.tgz
          tar -zxvf allure-2.13.2.tgz
          sudo mv allure-2.13.2 /opt/allure
          sudo ln -s /opt/allure/bin/allure /usr/local/bin/allure

      - name: Run tests
        run: |
          pytest --alluredir=./allure-results

      - name: Generate report
        run: |
          allure generate ./allure-results -o ./allure-report --clean

      - name: Upload report
        uses: actions/upload-artifact@v2
        with:
          name: allure-report
          path: allure-report

13.2 Jenkins 集成

Jenkinsfile

pipeline {
    agent any

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Install Dependencies') {
            steps {
                sh 'pip install -r requirements.txt'
            }
        }

        stage('Run Tests') {
            steps {
                sh 'pytest --alluredir=./allure-results'
            }
        }

        stage('Generate Report') {
            steps {
                sh 'allure generate ./allure-results -o ./allure-report --clean'
            }
        }
    }

    post {
        always {
            allure([
                includeProperties: false,
                jdk: '',
                properties: [],
                reportBuildPolicy: 'ALWAYS',
                results: [[path: 'allure-results']]
            ])
        }
    }
}

13.3 GitLab CI 集成

创建 .gitlab-ci.yml

stages:
  - test

test:
  stage: test
  image: python:3.9
  before_script:
    - pip install -r requirements.txt
    - apt-get update && apt-get install -y wget unzip
    - wget https://github.com/allure-framework/allure2/releases/download/2.13.2/allure-2.13.2.tgz
    - tar -zxvf allure-2.13.2.tgz
    - export PATH=$PATH:$(pwd)/allure-2.13.2/bin
  script:
    - pytest --alluredir=./allure-results
    - allure generate ./allure-results -o ./allure-report --clean
  artifacts:
    paths:
      - allure-report
    expire_in: 30 days

14. 常见问题和解决方案

14.1 问题 1:allure 命令找不到

问题描述

allure --version
# 错误: 'allure' 不是内部或外部命令,也不是可运行的程序

解决方案

  1. 检查 Allure 是否已安装
  2. 检查环境变量 PATH 是否包含 Allure 的 bin 目录
  3. 重新安装 Allure 并配置环境变量

Windows 解决方案

# 检查环境变量
$env:PATH -split ';' | Select-String "allure"

# 如果找不到,手动添加到 PATH
# 或者使用完整路径
C:allurebinallure.exe --version

14.2 问题 2:allure-results 目录为空

问题描述:运行 pytest 后,allure-results 目录为空或没有生成文件。

解决方案

  1. 检查是否安装了 allure-pytest
  2. 检查 pytest 命令中是否包含 --alluredir 参数
  3. 检查 pytest.ini 中是否配置了 addopts

验证步骤

# 1. 检查插件是否安装
pip show allure-pytest

# 2. 检查 pytest 插件列表
pytest --trace-config | grep allure

# 3. 手动指定参数运行
pytest test_example.py --alluredir=./allure-results -v

14.3 问题 3:报告中文乱码

问题描述:Allure 报告中中文显示为乱码。

解决方案

  1. 确保文件使用 UTF-8 编码
  2. 在 pytest.ini 中配置编码
  3. 在生成报告时指定编码

pytest.ini 配置

[pytest]
addopts = 
    --alluredir=./allure-results
    --tb=short

生成报告时

# 确保使用 UTF-8 编码
allure generate ./allure-results -o ./allure-report --clean

14.4 问题 4:历史记录不显示

问题描述:生成的报告中没有历史趋势图。

解决方案

  1. 确保 allure-report/history 目录存在
  2. 在生成新报告前,复制历史目录到 allure-results
  3. 生成报告时不要使用 –clean(或先复制历史)

脚本示例

# Windows
if exist allure-reporthistory (
    xcopy /E /I allure-reporthistory allure-resultshistory
)
pytest --alluredir=./allure-results
allure generate ./allure-results -o ./allure-report

# Linux/Mac
if [ -d "allure-report/history" ]; then
    cp -r allure-report/history allure-results/history
fi
pytest --alluredir=./allure-results
allure generate ./allure-results -o ./allure-report

14.5 问题 5:附件无法显示

问题描述:报告中附加的截图或文件无法显示。

解决方案

  1. 检查附件数据是否正确
  2. 检查附件类型是否正确
  3. 检查文件路径是否正确

示例

import allure

# 确保附件数据是字节类型
screenshot = driver.get_screenshot_as_png()  # 返回 bytes
allure.attach(
    screenshot,  # 必须是 bytes
    name="截图",
    attachment_type=allure.attachment_type.PNG
)

15. 最佳实践

15.1 项目结构建议

推荐的目录结构

project/
├── tests/
│   ├── __init__.py
│   ├── conftest.py
│   ├── test_login.py
│   ├── test_user.py
│   └── test_order.py
├── allure-results/      # Allure 结果数据(自动生成,可加入 .gitignore)
├── allure-report/       # Allure 报告(自动生成,可加入 .gitignore)
├── pytest.ini          # pytest 配置文件
├── requirements.txt    # 依赖文件
└── README.md           # 项目说明

.gitignore 配置

# Allure
allure-results/
allure-report/
*.log

15.2 测试代码规范

1. 使用描述性的标题和描述

@allure.title("测试管理员登录功能")
@allure.description("""
测试管理员登录功能:
1. 输入正确的管理员账号和密码
2. 验证登录成功
3. 检查用户权限
""")
def test_admin_login():
    pass

2. 合理使用步骤

def test_login():
    with allure.step("打开登录页面"):
        # 操作
    with allure.step("输入用户名和密码"):
        # 操作
    with allure.step("点击登录按钮"):
        # 操作
    with allure.step("验证登录成功"):
        # 断言

发表评论