Allure报告
Allure 测试报告 完整学习大纲
一、Allure 概述
1.1 什么是 Allure Report?
- 定义:Allure Report 是一个轻量级、支持多语言的开源测试报告框架,由 Yandex 团队开源,能把原始测试结果渲染成一份交互式的 HTML 报告。
- 核心思路:测试框架(pytest/TestNG/JUnit…)只负责产出结构化结果文件(JSON/XML),Allure 命令行工具负责渲染成报告——两者解耦,所以能跨语言通用。
- 现状:目前主流版本是 Allure 2(即 Allure Report 2.x),通常说的 “Allure” 指的就是它。
1.2 为什么需要它?
原生 pytest 输出是这样的:
test_login.py::test_login_success PASSED [ 33%]
test_login.py::test_login_wrong_pwd FAILED [ 66%]
test_login.py::test_login_empty PASSED [100%]
问题:只有通过/失败,看不出业务含义、看不到失败截图、不知道哪一步失败、无法给非技术同事看。
Allure 解决的是:让测试报告从”给测试自己看”变成”给整个团队看”。
1.3 报告工具对比
| 工具 | 输出 | 优点 | 缺点 |
|---|---|---|---|
| Allure | 交互式 HTML | ★ 美观、步骤/附件/历史趋势/分类齐全 | 需装命令行工具(依赖 Java) |
| pytest-html | 单页 HTML | 零配置,一个插件搞定 | 信息量少,无趋势,附件弱 |
| HTMLTestRunner | HTML | Python 老牌 | 样式老旧,维护停滞 |
| JUnit XML | XML | CI 通用、机器可读 | 不是给人看的 |
| 自研报告 | 自定义 | 灵活 | 成本高 |
💡 选型建议:项目要出正式测试报告、需要给产品或领导看 → Allure;只是快速看结果 → pytest-html 足够。
1.4 核心特性
- 分层组织:Epic / Feature / Story 按业务维度组织用例
- 步骤(Step):展示用例执行的每一步,失败定位到具体步骤
- 附件(Attachment):截图、日志、请求响应、视频
- 严重等级:BLOCKER / CRITICAL / NORMAL / MINOR / TRIVIAL
- 缺陷分类(Categories):把失败原因归类(如”产品缺陷”/”测试脚本问题”)
- 历史趋势:多次执行对比,看通过率变化
- 时间轴(Timeline):并发执行时看时序
- 环境信息:记录执行环境(版本、浏览器、URL)
1.5 生态支持
Python: pytest (allure-pytest) ★ 本文重点
Java: TestNG / JUnit4 / JUnit5 / Cucumber JVM
JS: Jest / Mocha / Cypress / Playwright
PHP: PHPUnit
.NET: NUnit / xUnit
其他: Robot Framework / Behave / RSpec
二、环境搭建
2.1 安装(两步:Python 插件 + 命令行工具)
# 第一步:安装 pytest 插件(负责产出结果文件)
pip install allure-pytest
# 第二步:安装 Allure 命令行工具(负责渲染报告)
# Windows(需先装 Scoop,或用 winget)
scoop install allure
# 或
winget install Qameta.Allure
# macOS
brew install allure
# Linux / 手动(通用)
# 从 https://github.com/allure-framework/allure2/releases 下载 zip
# 解压后把 bin 目录加入 PATH
⚠️ 关键点:Allure 命令行工具是 Java 写的,运行必须依赖 JDK 8+。很多”命令找不到”“报告生成失败”的问题根源是没装 Java 或版本不对。
# 验证
allure --version
java -version # 必须是 8 或以上
2.2 常见问题
| 现象 | 原因 | 解决 |
|---|---|---|
allure: command not found |
未装命令行工具或未配 PATH | 重装并把 bin 加入 PATH |
'allure' 不是内部或外部命令 |
同上(Windows) | 检查环境变量,重开终端 |
| 提示 Java 相关错误 | 未装 JDK 或版本过低 | 装 JDK 8+ |
allure serve 打不开浏览器 |
端口被占用 | allure serve --port 8081 <dir> |
| 报告是空的 | 路径写错,或没跑 pytest | 确认 allure-results 下有 .json 文件 |
三、快速上手(三步出报告)
3.1 写用例
# test_login.py
import allure
@allure.feature("登录模块")
@allure.story("用户登录")
@allure.title("使用正确的账号密码登录成功")
def test_login_success():
with allure.step("输入用户名"):
assert True
with allure.step("输入密码"):
assert True
with allure.step("点击登录按钮"):
assert True
with allure.step("断言跳转到首页"):
assert True
@allure.feature("登录模块")
@allure.story("用户登录")
@allure.title("密码错误时登录失败")
def test_login_wrong_pwd():
assert False, "预期登录失败,实际却成功了"
3.2 执行并生成结果
# 第一步:跑测试,把结果写到 allure-results 目录
pytest test_login.py --alluredir=./allure-results --clean-alluredir
参数说明:
--alluredir=./allure-results 指定结果输出目录
--clean-alluredir 每次执行前清空旧结果(强烈建议加,否则历史数据会混进来)
执行后目录内容:
allure-results/
├── 4a7c1f2e-xxxx-result.json 每条用例一个结果文件
├── 8b9d3c4e-xxxx-result.json
├── environment.properties 环境信息(可选,需自己生成)
├── categories.json 缺陷分类(可选)
└── history/ 历史趋势数据(需自己复制)
3.3 查看报告(三种方式)
# 方式一:临时起服务并自动打开浏览器(本地查看最方便)
allure serve ./allure-results
# 方式二:生成静态 HTML 报告(CI 归档用)
allure generate ./allure-results -o ./allure-report --clean
# 方式三:打开已生成的静态报告
allure open ./allure-report
| 命令 | 用途 | 场景 |
|---|---|---|
allure serve |
起临时服务,自动开浏览器 | 本地调试 |
allure generate |
生成静态文件到目录 | CI 归档、发布 |
allure open |
打开已生成的报告 | 查看历史归档 |
⚠️
allure generate的-o目录建议加--clean,否则旧文件会残留。另外生成的报告用双击 HTML 直接打开会因跨域限制看不到内容,必须走allure open或 HTTP 服务。
四、报告页面结构详解
4.1 七大页面
| 页面 | 作用 | 常用场景 |
|---|---|---|
| Overview 总览 | 通过率、趋势、环境、套件统计 | 给领导看的第一页 |
| Categories 分类 | 按缺陷类型归类失败用例 | 区分”产品 Bug”还是”脚本问题” |
| Suites 套件 | 按测试套件/类/方法组织 | 开发定位用 |
| Graphs 图表 | 状态分布、严重等级、耗时、重试 | 分析质量分布 |
| Timeline 时间轴 | 按时间线展示执行过程 | 并发执行时看时序 |
| Behaviors 行为 | 按 Epic/Feature/Story 组织 | 按业务维度看覆盖率 |
| Packages 包 | 按代码包结构组织 | 开发定位用 |
4.2 Overview 页要点
┌────────────────────────────────────────────┐
│ 执行信息:开始时间、持续时间、总用例数 │
│ 环形图:通过 / 失败 / 跳过 / 损坏 占比 │
│ Trend:历史趋势折线图(需配置 history) │
│ 环境信息:Environment 卡片(需自己生成) │
│ 套件统计:各套件通过率 │
│ 分类统计:Categories 各类型数量 │
└────────────────────────────────────────────┘
4.3 Behaviors 页(业务视角,最推荐)
Epic: 电商系统 ← 一级:系统/大模块
└─ Feature: 订单模块 ← 二级:功能模块
└─ Story: 用户下单 ← 三级:用户故事
├─ 用例:使用优惠券下单成功
├─ 用例:库存不足时下单失败
└─ 用例:未登录时下单跳转登录页
💡 测试价值:Behaviors 页让产品经理也能看懂测试覆盖了哪些业务场景,是跨角色沟通的桥梁。建议所有用例都打上 Epic/Feature/Story。
4.4 用例详情页
点开一条用例,能看到:
- Status:通过/失败及失败原因
- Description:用例描述
- Steps:执行步骤(失败会标红并展开)
- Attachments:截图、日志等附件
- Links:关联的缺陷单、需求单
- Labels:标签、严重等级、所属套件
- Duration:耗时
- History/Retries:历史趋势、重试记录
五、核心装饰器 API(重点)
5.1 业务分层:epic / feature / story
import allure
@allure.epic("电商系统")
@allure.feature("订单模块")
@allure.story("用户下单")
def test_create_order():
...
| 装饰器 | 层级 | 建议粒度 |
|---|---|---|
@allure.epic |
一级 | 整个系统/大业务线 |
@allure.feature |
二级 | 功能模块(订单、支付、用户) |
@allure.story |
三级 | 用户故事(下单、取消订单、退款) |
也可以用
@allure.parent_suite/@allure.suite/@allure.sub_suite在 Suites 页做类似分层。
5.2 标题:title
@allure.title("使用正确的账号密码登录成功")
def test_login():
...
# 参数化时动态生成标题(很实用)
@pytest.mark.parametrize("username,password,expect", [
("admin", "123456", True),
("admin", "wrong", False),
("", "123456", False),
])
@allure.title("登录测试:账号={username},密码={password}")
def test_login_params(username, password, expect):
...
💡 不加
@allure.title时,报告里显示的是函数名(test_login_params[admin-123456-True]),可读性差。参数化用例强烈建议加动态标题。
5.3 描述:description
@allure.description("""
验证用户在未登录状态下访问订单页面,应跳转到登录页。
前置条件:清除浏览器 Cookie
优先级:P0
""")
def test_order_need_login():
...
# 也支持 HTML 富文本
@allure.description_html("""
<h2>测试目的</h2>
<p>验证<b>订单</b>模块的边界值</p>
<table border="1"><tr><th>场景</th><th>预期</th></tr>
<tr><td>数量为0</td><td>提示错误</td></tr></table>
""")
def test_order_boundary():
...
5.4 严重等级:severity
import allure
@allure.severity(allure.severity_level.BLOCKER)
def test_payment():
...
| 等级 | 含义 | 对应优先级 |
|---|---|---|
BLOCKER |
阻塞,系统不可用 | P0 |
CRITICAL |
严重,核心功能失效 | P0 |
NORMAL |
一般,功能缺陷 | P1(默认) |
MINOR |
次要,界面/文案问题 | P2 |
TRIVIAL |
轻微,建议类 | P3 |
# 按严重等级筛选执行
pytest --allure-severities=blocker,critical
5.5 步骤:step
# 方式一:with 上下文(推荐,能包裹多条语句)
def test_login():
with allure.step("打开登录页"):
print("open /login")
with allure.step("输入账号密码"):
print("input")
with allure.step("点击登录"):
print("click")
# 方式二:装饰器(适合封装好的函数)
@allure.step("登录用户:{username}")
def login(username, password):
print(f"login {username}")
def test_use_step():
login("admin", "123456") # 该调用会在报告里显示为一个步骤
# 嵌套步骤
with allure.step("下单流程"):
with allure.step("选择商品"):
...
with allure.step("提交订单"):
...
💡 核心价值:失败时报告会精确标红失败的那一步,不用再翻日志猜是哪一步挂的。这是 Allure 最实用的功能之一。
5.6 附件:attach(重点)
import allure
# 文本
allure.attach("这是一段日志", name="执行日志",
attachment_type=allure.attachment_type.TEXT)
# HTML
allure.attach("<h1>标题</h1><p>内容</p>", name="HTML片段",
attachment_type=allure.attachment_type.HTML)
# JSON
allure.attach(json.dumps(data, ensure_ascii=False, indent=2), name="响应数据",
attachment_type=allure.attachment_type.JSON)
# 图片(传 bytes)
allure.attach(png_bytes, name="截图",
attachment_type=allure.attachment_type.PNG)
# 从文件读取
allure.attach.file("./logs/app.log", name="应用日志",
attachment_type=allure.attachment_type.TEXT)
常用 attachment_type:
| 类型 | 用途 | 类型 | 用途 |
|---|---|---|---|
TEXT |
纯文本/日志 | PNG |
截图(PNG) |
HTML |
HTML 片段 | JPG |
截图(JPG) |
JSON |
JSON 数据 | CSV |
表格数据 |
XML |
XML 数据 | PDF |
PDF 文档 |
YAML |
YAML 配置 | ZIP |
压缩包 |
5.7 链接:link / issue / testcase
@allure.link("https://wiki.example.com/order", name="需求文档")
@allure.issue("https://jira.example.com/browse/BUG-1234", name="BUG-1234")
@allure.testcase("https://tapd.example.com/case/5678", name="TC-5678")
def test_order():
...
| 装饰器 | 显示位置 | 用途 |
|---|---|---|
link |
Links 区(普通图标) | 需求文档、Wiki |
issue |
Links 区(Bug 图标) | 关联缺陷单 |
testcase |
Links 区(用例图标) | 关联用例管理平台 |
💡 测试价值:把失败用例和 Jira 单号关联起来,开发点开报告就能直接跳到缺陷详情,沟通成本大幅降低。
5.8 标签:tag / label
@allure.tag("冒烟", "P0", "回归")
@allure.label("owner", "张三")
@allure.label("env", "test")
def test_smoke():
...
# 按标签筛选执行
pytest --allure-tags=冒烟
pytest --allure-tags=冒烟,P0 # 多标签(或关系)
5.9 运行时动态设置
装饰器只能写死,参数化/循环场景下需要用 dynamic:
import allure
@pytest.mark.parametrize("case", cases)
def test_dynamic(case):
allure.dynamic.title(case["name"]) # 动态标题
allure.dynamic.description(case["desc"]) # 动态描述
allure.dynamic.severity(case["severity"]) # 动态等级
allure.dynamic.feature(case["module"]) # 动态模块
allure.dynamic.tag(case["tag"])
allure.dynamic.link(case["doc_url"], name="文档")
...
六、实战:失败自动截图(重点)
这是 Allure 在 UI 自动化中最有价值的用法——用例失败时自动把截图贴进报告。
6.1 完整实现(conftest.py)
# conftest.py
import allure
import pytest
from selenium import webdriver
# ---------- 1. 浏览器 fixture ----------
@pytest.fixture(scope="function")
def driver():
d = webdriver.Chrome()
d.maximize_window()
yield d
d.quit()
# ---------- 2. 失败时自动截图 ----------
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""把每个阶段(setup/call/teardown)的执行结果挂到 item 上"""
outcome = yield
report = outcome.get_result()
setattr(item, "rep_" + report.when, report) # rep_setup / rep_call / rep_teardown
@pytest.fixture(autouse=True)
def screenshot_on_failure(driver, request):
"""用例执行后自动触发,失败则截图"""
yield
# 只在用例主体(call)失败时才截图
if hasattr(request.node, "rep_call") and request.node.rep_call.failed:
try:
png = driver.get_screenshot_as_png() # 也可用 save_screenshot(path) 再读文件
allure.attach(png, name="失败截图",
attachment_type=allure.attachment_type.PNG)
except Exception as e:
# 浏览器已崩溃时截图会失败,别让它影响报告生成
allure.attach(f"截图失败: {e}", name="截图异常",
attachment_type=allure.attachment_type.TEXT)
原理说明:
1. pytest_runtest_makereport 是 pytest 的钩子,在每个阶段执行完后被调用
2. hookwrapper=True 让我们能拿到执行结果(report)
3. 把结果存到 item.rep_call(call 阶段 = 用例主体)
4. screenshot_on_failure 是 autouse fixture,在用例后执行
5. 检查 rep_call.failed → 失败则截图 → allure.attach 贴进报告
6.2 用例中使用
# test_baidu.py
import allure
from selenium.webdriver.common.by import By
@allure.epic("搜索功能")
@allure.feature("百度搜索")
@allure.story("搜索关键字")
@allure.title("搜索'pytest'应显示结果")
def test_search(driver):
with allure.step("打开百度首页"):
driver.get("https://www.baidu.com")
with allure.step("输入关键字 pytest"):
driver.find_element(By.ID, "kw").send_keys("pytest")
with allure.step("点击搜索按钮"):
driver.find_element(By.ID, "su").click()
with allure.step("断言结果页包含pytest"):
assert "pytest" in driver.title # 失败时报告里会自动附截图
pytest test_baidu.py --alluredir=./allure-results --clean-alluredir
allure serve ./allure-results
6.3 接口测试:附请求与响应
# conftest.py
import allure
import requests
@pytest.fixture
def api():
session = requests.Session()
yield session
session.close()
@pytest.fixture(autouse=True)
def attach_api_log(request):
"""记录每次接口调用的请求与响应"""
logs = []
def record(method, url, **kwargs):
resp = requests.request(method, url, **kwargs)
logs.append(
f"【请求】{method.upper()} {url}\n"
f" headers: {dict(kwargs.get('headers', {}))}\n"
f" body: {kwargs.get('json') or kwargs.get('data')}\n"
f"【响应】{resp.status_code}\n"
f" body: {resp.text[:2000]}\n"
)
return resp
globals()["_record_request"] = record
yield
if logs:
allure.attach("\n".join(logs), name="接口调用日志",
attachment_type=allure.attachment_type.TEXT)
# 更简洁的做法:直接在用例里贴
def test_query_order(api):
resp = api.get("https://api.example.com/orders/1")
allure.attach(
json.dumps(resp.json(), ensure_ascii=False, indent=2),
name="响应数据", attachment_type=allure.attachment_type.JSON
)
assert resp.status_code == 200
6.4 其他常用附件场景
# 附页面源码(排查元素定位失败很有用)
allure.attach(driver.page_source, name="页面源码",
attachment_type=allure.attachment_type.HTML)
# 附浏览器日志
allure.attach("\n".join(driver.get_log("browser")), name="Console日志",
attachment_type=allure.attachment_type.TEXT)
# 附 App 自动化截图(UIAutomator2)
allure.attach(d.screenshot(format="raw"), name="App截图",
attachment_type=allure.attachment_type.PNG)
# 附日志文件
allure.attach.file("./logs/test.log", name="完整日志",
attachment_type=allure.attachment_type.TEXT)
七、环境信息与缺陷分类
7.1 environment.properties(环境信息)
# 方式一:在 allure-results 目录手动创建
cat > ./allure-results/environment.properties <<'EOF'
system.version=2.3.0
test.env=TEST
base.url=https://test.example.com
browser=Chrome 120
python.version=3.11.5
executor=张三
EOF
# 方式二:用 conftest 自动生成(推荐,动态获取真实值)
# conftest.py
import platform
import pytest
def pytest_configure(config):
"""生成环境信息文件"""
import os
results_dir = config.getoption("--alluredir")
if not results_dir:
return
os.makedirs(results_dir, exist_ok=True)
env = {
"system.version": "2.3.0",
"test.env": "TEST",
"python.version": platform.python_version(),
"platform": platform.platform(),
"executor": os.getenv("USER", "unknown"),
}
with open(os.path.join(results_dir, "environment.properties"),
"w", encoding="utf-8") as f:
for k, v in env.items():
f.write(f"{k}={v}\n")
7.2 categories.json(缺陷分类)
默认只有”Product defects”和”Test defects”。自定义后可按失败原因归类,便于区分责任:
[
{
"name": "产品缺陷",
"matchedStatuses": ["failed"],
"messageRegex": ".*AssertionError.*"
},
{
"name": "脚本问题(元素找不到)",
"matchedStatuses": ["broken"],
"messageRegex": ".*NoSuchElementException.*"
},
{
"name": "环境问题",
"matchedStatuses": ["broken", "failed"],
"messageRegex": ".*(ConnectionError|Timeout|ConnectionRefused).*"
},
{
"name": "接口异常",
"matchedStatuses": ["failed"],
"messageRegex": ".*状态码.*5\\d\\d.*"
},
{
"name": "忽略的用例",
"matchedStatuses": ["skipped"]
}
]
💡 测试价值:Categories 页能一眼看出”这次失败里,3 个是真 Bug、2 个是脚本问题、1 个是环境不稳定”——写测试报告时直接引用。
7.3 executor.json(CI 执行信息)
{
"name": "Jenkins",
"type": "jenkins",
"url": "https://jenkins.example.com/job/api-test/123/",
"buildName": "api-test #123",
"buildUrl": "https://jenkins.example.com/job/api-test/123/",
"reportUrl": "https://jenkins.example.com/job/api-test/123/allure/",
"buildOrder": 123
}
# conftest.py 自动生成
import json
import os
def pytest_configure(config):
results_dir = config.getoption("--alluredir")
if not results_dir:
return
executor = {
"name": "Jenkins",
"type": "jenkins",
"buildName": os.getenv("JOB_NAME", "local"),
"buildUrl": os.getenv("BUILD_URL", ""),
"buildOrder": os.getenv("BUILD_NUMBER", ""),
}
with open(os.path.join(results_dir, "executor.json"), "w", encoding="utf-8") as f:
json.dump(executor, f, ensure_ascii=False, indent=2)
7.4 历史趋势(Trend)
Allure 的 Trend 折线图需要把上一次报告的 history 复制过来:
# 执行流程
# 1. 把上次的 history 复制到本次结果目录
cp -r ./allure-report/history ./allure-results/history
# 2. 跑测试
pytest --alluredir=./allure-results --clean-alluredir
# 注意:--clean-alluredir 会清空,所以上面的复制要在之后做,
# 或者用 --clean-alluredir 之后再从备份目录复制
# 3. 生成报告(会同时产生新的 history)
allure generate ./allure-results -o ./allure-report --clean
# 标准做法:results 每次清空 → 从 report 目录回填 history
# conftest.py
def pytest_configure(config):
import shutil, os
results_dir = config.getoption("--alluredir")
history_src = "./allure-report/history" # 上次报告里的
history_dst = os.path.join(results_dir or "", "history")
if os.path.exists(history_src) and results_dir:
os.makedirs(results_dir, exist_ok=True)
if os.path.exists(history_dst):
shutil.rmtree(history_dst)
shutil.copytree(history_src, history_dst)
⚠️ 顺序很重要:先 clean results → 再回填 history → 再跑测试 → 再 generate。
7.5 重试趋势(Retries)
配合 pytest-rerunfailures 自动重跑,报告里会显示重试记录:
pip install pytest-rerunfailures
pytest --reruns=2 --reruns-delay=1 --alluredir=./allure-results
报告里能看到”该用例重试了 N 次最终通过”——能识别出不稳定用例(flaky test),这是很有价值的质量信号。
八、与 pytest 的集成配置
8.1 命令行参数
pytest \
--alluredir=./allure-results \ # 结果输出目录
--clean-alluredir \ # 清空旧结果
--allure-severities=blocker,critical \ # 只跑指定等级
--allure-tags=冒烟 \ # 只跑指定标签
--allure-features=订单模块 \ # 只跑指定 feature
--allure-stories=用户下单 \ # 只跑指定 story
--allure-link-pattern=issue:https://jira.example.com/browse/{} # 链接模板
--allure-link-pattern很好用:配了之后,@allure.issue("BUG-1234")会自动拼成完整 URL。
8.2 pytest.ini 配置
[pytest]
addopts = --alluredir=./allure-results --clean-alluredir
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
markers =
smoke: 冒烟测试
regression: 回归测试
8.3 与 fixture 结合(典型结构)
# conftest.py
import allure
import pytest
@pytest.fixture(scope="session")
def env_config():
return {"base_url": "https://test.example.com", "env": "TEST"}
@pytest.fixture
def login_user(env_config):
"""登录并返回 token"""
with allure.step("前置:用户登录"):
token = "mock_token"
allure.attach(token, name="登录token",
attachment_type=allure.attachment_type.TEXT)
yield token
with allure.step("后置:退出登录"):
...
8.4 并发执行
pip install pytest-xdist
pytest -n 4 --alluredir=./allure-results --clean-alluredir
并发时 Timeline 页特别有用,能看清各进程的执行时序和耗时分布。
九、CI/CD 集成
9.1 Jenkins
# 1. 安装 Allure Jenkins Plugin
# 系统管理 → 插件管理 → 搜索 "Allure"
# 2. 全局工具配置里添加 Allure Commandline
# 系统管理 → 全局工具配置 → Allure Commandline → 新增
# 3. 流水线中使用
pipeline {
agent any
stages {
stage('Test') {
steps {
sh 'pytest --alluredir=./allure-results --clean-alluredir'
}
post {
always {
// 生成报告并归档
allure includeProperties: false,
results: [[path: 'allure-results']]
}
}
}
}
}
详细 Jenkins 配置见 Jenkins。
9.2 GitLab CI
stages:
- test
api-test:
stage: test
image: python:3.11
script:
- pip install -r requirements.txt
- pytest --alluredir=./allure-results --clean-alluredir
artifacts:
when: always
paths:
- allure-results/
- allure-report/
expire_in: 30 days
after_script:
- allure generate ./allure-results -o ./allure-report --clean
9.3 GitHub Actions
name: API Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests
run: pytest --alluredir=./allure-results --clean-alluredir
continue-on-error: true
- name: Get Allure history
uses: actions/checkout@v4
if: always()
continue-on-error: true
with:
ref: gh-pages
path: gh-pages
- name: Build Allure report
uses: simple-elf/allure-report-action@master
if: always()
with:
allure_results: allure-results
gh_pages: gh-pages
allure_report: allure-report
- name: Upload report
uses: actions/upload-artifact@v4
if: always()
with:
name: allure-report
path: allure-report
9.4 报告归档与通知
实践建议:
1. 每次构建归档 allure-results(体积小)
2. 保留 allure-report/history 目录,实现跨构建趋势
3. 构建结束后推送通知到企业微信/钉钉:
消息里带上 通过率、失败数、报告链接
# notify.py —— 解析报告摘要并推送
import json
import glob
import os
import requests
def summary(results_dir="./allure-results") -> dict:
"""统计本次执行结果"""
stat = {"passed": 0, "failed": 0, "broken": 0, "skipped": 0}
for f in glob.glob(os.path.join(results_dir, "*-result.json")):
with open(f, encoding="utf-8") as fp:
stat[json.load(fp)["status"]] = stat.get(
json.load(fp)["status"], 0) + 1
total = sum(stat.values())
stat["total"] = total
stat["pass_rate"] = f"{stat['passed'] / total * 100:.1f}%" if total else "0%"
return stat
def send_to_wechat(webhook: str, content: str):
requests.post(webhook, json={
"msgtype": "markdown",
"markdown": {"content": content}
})
if __name__ == "__main__":
s = summary()
msg = (
f"### 自动化测试报告\n"
f"> 用例总数:**{s['total']}**\n"
f"> 通过:{s['passed']} 失败:{s['failed']} "
f"损坏:{s['broken']} 跳过:{s['skipped']}\n"
f"> 通过率:**{s['pass_rate']}**\n"
f"> [查看完整报告](https://jenkins.example.com/job/api-test/allure/)"
)
send_to_wechat(os.getenv("WECHAT_WEBHOOK"), msg)
十、其他框架接入(简述)
| 框架 | 依赖 | 关键用法 |
|---|---|---|
| pytest | allure-pytest |
装饰器 + allure.attach |
| TestNG | allure-testng |
@Epic/@Feature/@Step、@Attachment |
| JUnit5 | allure-junit5 |
@Epic/@Step、Allure.addAttachment() |
| Cucumber JVM | allure-cucumber-jvm |
自动按 Feature/Scenario 分层 |
| Pytest-BDD | allure-pytest |
与 BDD 无缝,Scenario 即 Story |
| Playwright | allure-playwright |
自带截图/视频/网络日志附件 |
| Robot Framework | allure-robotframework |
--listener allure_robotframework |
// TestNG 示例(对照理解)
@Epic("电商系统")
@Feature("订单模块")
public class OrderTest {
@Test(description = "创建订单")
@Story("用户下单")
@Severity(SeverityLevel.CRITICAL)
@Description("验证正常下单流程")
public void testCreateOrder() {
step("打开下单页");
step("提交订单");
}
@Step("{action}")
public void step(String action) { }
@Attachment(value = "失败截图", type = "image/png")
public byte[] screenshot() {
return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
}
}
十一、最佳实践与常见问题
11.1 最佳实践清单
- 每条用例都加
@allure.title(尤其参数化的,用动态标题) - 业务分层打全:epic / feature / story
- 关键操作用
allure.step包裹,失败能定位到步 - UI 用例配置失败自动截图
- 接口用例附上请求与响应(排查效率翻倍)
- 标注 severity,便于按等级筛选执行
- 关联 issue / testcase,与缺陷平台打通
- 生成 environment.properties,报告自带环境信息
- 自定义 categories.json,区分真 Bug 与脚本问题
- CI 里保留 history,形成趋势图
- 每次执行加
--clean-alluredir,避免脏数据
11.2 常见问题速查
| 问题 | 原因 | 解决 |
|---|---|---|
allure 命令找不到 |
未装命令行工具 / PATH 未配 | 重装并配 PATH |
| 提示 Java 错误 | 未装 JDK 或版本低 | 装 JDK 8+ |
| 报告里没有步骤 | 没用 allure.step |
用 with allure.step() 包裹 |
| 报告是空的 | --alluredir 路径错,或没跑 pytest |
检查目录下有没有 *-result.json |
| 截图没显示 | fixture 作用域/hook 没配对 | 检查 pytest_runtest_makereport 钩子 |
| 参数化标题是一串参数 | 没写动态 title | @allure.title("...{param}") |
| 趋势图不显示 | 没回填 history | 复制上次 report/history 到 results |
| 旧报告数据混进来了 | 没加 --clean-alluredir |
加上该参数 |
| 直接双击 HTML 打不开 | 跨域限制 | 用 allure open 或起 HTTP 服务 |
| 报告生成很慢 | 用例太多、附件太大 | 压缩图片、清理无用附件 |
| 中文乱码 | 文件编码问题 | properties/json 显式用 UTF-8 写 |
| 并发时报告重复 | xdist 与旧数据冲突 | 确保 --clean-alluredir + 结果目录独立 |
11.3 一条推荐的执行脚本
#!/bin/bash
# run_tests.sh —— 标准的测试执行 + 报告生成流程
set -e
RESULTS=./allure-results
REPORT=./allure-report
HISTORY_BACKUP=./.history_backup
# 1. 备份上次的 history
[ -d "$REPORT/history" ] && cp -r "$REPORT/history" "$HISTORY_BACKUP"
# 2. 跑测试(清空旧结果)
pytest --alluredir=$RESULTS --clean-alluredir -v
# 3. 回填 history 以实现趋势
[ -d "$HISTORY_BACKUP" ] && cp -r "$HISTORY_BACKUP" "$RESULTS/history"
# 4. 生成报告
allure generate $RESULTS -o $REPORT --clean
# 5. 输出摘要
echo "报告已生成:$REPORT/index.html"
allure open $REPORT &
十二、学习资源
| 资源 | 说明 |
|---|---|
| Allure 官方文档 | 权威,含各语言接入指南 |
| allure-pytest 文档 | Python 用法完整参考 |
| allure-python GitHub | 源码与 issue |
| Allure2 发布页 | 命令行工具下载 |
| Jenkins Allure 插件 | CI 集成 |
| pytest 文档 | 本站 pytest 篇 |
| Selenium | 配合截图实战 |