
allure测试报告美化与定制
大约 9 分钟
allure测试报告美化与定制
前言:让测试报告"颜值爆表"
还记得我刚开始做自动化测试时,测试报告就是控制台输出的一堆文字,想要给领导展示测试结果时,只能截图或者复制粘贴到Word文档里。那画面,简直不忍直视。
直到我遇到了allure,这个测试报告界的"颜值担当"。第一次看到allure生成的报告时,我的内心是震撼的:原来测试报告还可以这么漂亮!从此,我就成了allure的忠实粉丝。
今天,我就来分享一下如何用allure打造出让人眼前一亮的测试报告,让你的测试结果不仅有内涵,更有颜值。
allure基础概念
什么是allure?
allure是一个轻量级、多语言的测试报告工具,它可以生成美观、交互式的HTML测试报告。就像给你的测试结果穿上了一件华丽的外衣。
allure的核心优势
- 颜值超高:现代化的UI设计,看起来就很专业
- 信息丰富:不仅有测试结果,还有执行时间、环境信息、历史趋势等
- 交互性强:可以点击查看详细信息,支持筛选和搜索
- 多语言支持:Python、Java、JavaScript等都支持
- 集成简单:几行代码就能集成到现有项目中
allure安装与基础使用
1. 安装allure
# 安装allure-pytest插件
pip install allure-pytest
# 安装allure命令行工具(macOS)
brew install allure
# 安装allure命令行工具(Windows)
# 下载并解压allure,配置环境变量
# 验证安装
allure --version2. 基础使用
import allure
import pytest
@allure.feature("用户管理")
@allure.story("用户登录")
@allure.title("测试用户登录成功")
@allure.description("验证用户使用正确的用户名和密码能够成功登录")
@allure.severity(allure.severity_level.CRITICAL)
def test_user_login_success():
with allure.step("输入用户名和密码"):
username = "admin"
password = "123456"
with allure.step("点击登录按钮"):
response = login(username, password)
with allure.step("验证登录结果"):
assert response.status_code == 200
assert "token" in response.json()生成报告:
# 运行测试并生成allure数据
pytest --alluredir=./allure-results
# 生成HTML报告
allure generate ./allure-results -o ./allure-report --clean
# 启动报告服务器
allure serve ./allure-resultsallure装饰器详解
1. 测试分层装饰器
import allure
@allure.epic("电商平台") # 史诗级别,最高层级
@allure.feature("用户管理") # 功能模块
@allure.story("用户注册") # 用户故事
class TestUserRegistration:
@allure.title("测试用户注册成功")
@allure.description("验证用户能够成功注册新账户")
def test_register_success(self):
pass
@allure.title("测试邮箱格式验证")
@allure.description("验证系统能够正确验证邮箱格式")
def test_email_validation(self):
pass2. 严重程度装饰器
@allure.severity(allure.severity_level.BLOCKER) # 阻塞级别
def test_critical_function():
"""系统核心功能测试"""
pass
@allure.severity(allure.severity_level.CRITICAL) # 严重级别
def test_important_function():
"""重要功能测试"""
pass
@allure.severity(allure.severity_level.NORMAL) # 普通级别
def test_normal_function():
"""普通功能测试"""
pass
@allure.severity(allure.severity_level.MINOR) # 次要级别
def test_minor_function():
"""次要功能测试"""
pass
@allure.severity(allure.severity_level.TRIVIAL) # 轻微级别
def test_trivial_function():
"""轻微功能测试"""
pass3. 标签和链接装饰器
@allure.tag("smoke", "regression", "api")
@allure.label("owner", "张三")
@allure.label("layer", "api")
@allure.link("https://jira.company.com/PROJ-123", name="需求链接")
@allure.issue("https://jira.company.com/BUG-456", name="缺陷链接")
@allure.testcase("https://testcase.company.com/TC-789", name="测试用例")
def test_with_metadata():
"""带有丰富元数据的测试用例"""
pass测试步骤与附件
1. 测试步骤
import allure
import requests
@allure.feature("API测试")
@allure.story("用户API")
def test_user_crud_operations():
"""用户CRUD操作测试"""
user_data = {"name": "张三", "email": "zhangsan@example.com"}
user_id = None
with allure.step("创建用户"):
response = requests.post("/api/users", json=user_data)
assert response.status_code == 201
user_id = response.json()["id"]
allure.attach(
json.dumps(response.json(), ensure_ascii=False, indent=2),
name="创建用户响应",
attachment_type=allure.attachment_type.JSON
)
with allure.step("查询用户"):
response = requests.get(f"/api/users/{user_id}")
assert response.status_code == 200
assert response.json()["name"] == user_data["name"]
with allure.step("更新用户"):
updated_data = {"name": "李四", "email": "lisi@example.com"}
response = requests.put(f"/api/users/{user_id}", json=updated_data)
assert response.status_code == 200
with allure.step("删除用户"):
response = requests.delete(f"/api/users/{user_id}")
assert response.status_code == 2042. 附件管理
import allure
import json
import requests
from PIL import Image
import io
class AllureAttachmentHelper:
"""allure附件助手"""
@staticmethod
def attach_json(data, name="JSON数据"):
"""附加JSON数据"""
json_str = json.dumps(data, ensure_ascii=False, indent=2)
allure.attach(json_str, name=name, attachment_type=allure.attachment_type.JSON)
@staticmethod
def attach_text(text, name="文本信息"):
"""附加文本信息"""
allure.attach(text, name=name, attachment_type=allure.attachment_type.TEXT)
@staticmethod
def attach_html(html, name="HTML内容"):
"""附加HTML内容"""
allure.attach(html, name=name, attachment_type=allure.attachment_type.HTML)
@staticmethod
def attach_image(image_path, name="截图"):
"""附加图片"""
with open(image_path, "rb") as f:
allure.attach(f.read(), name=name, attachment_type=allure.attachment_type.PNG)
@staticmethod
def attach_request_response(response, request_name="请求信息", response_name="响应信息"):
"""附加请求和响应信息"""
# 请求信息
request_info = {
"method": response.request.method,
"url": response.request.url,
"headers": dict(response.request.headers),
"body": response.request.body.decode() if response.request.body else None
}
AllureAttachmentHelper.attach_json(request_info, request_name)
# 响应信息
response_info = {
"status_code": response.status_code,
"headers": dict(response.headers),
"body": response.text
}
AllureAttachmentHelper.attach_json(response_info, response_name)
# 使用示例
def test_api_with_attachments():
"""带附件的API测试"""
with allure.step("发送API请求"):
response = requests.get("https://httpbin.org/json")
# 附加请求和响应信息
AllureAttachmentHelper.attach_request_response(response)
# 附加自定义信息
AllureAttachmentHelper.attach_text(
f"响应时间: {response.elapsed.total_seconds()}秒",
"性能信息"
)
with allure.step("验证响应结果"):
assert response.status_code == 200
AllureAttachmentHelper.attach_json(response.json(), "响应JSON")动态测试信息
1. 动态标题和描述
import allure
import pytest
@pytest.mark.parametrize("username,password,expected_status", [
("admin", "123456", 200),
("user", "password", 200),
("invalid", "wrong", 401)
])
def test_login_with_dynamic_title(username, password, expected_status):
"""动态标题测试"""
# 动态设置测试标题
allure.dynamic.title(f"测试用户登录: {username}")
# 动态设置描述
allure.dynamic.description(f"使用用户名 '{username}' 和密码 '{password}' 进行登录测试")
# 动态设置标签
if expected_status == 200:
allure.dynamic.tag("positive")
else:
allure.dynamic.tag("negative")
# 执行测试
response = login(username, password)
assert response.status_code == expected_status
def test_with_dynamic_metadata():
"""动态元数据测试"""
# 根据测试环境动态设置信息
env = os.getenv("TEST_ENV", "dev")
allure.dynamic.feature(f"环境测试-{env.upper()}")
allure.dynamic.story(f"{env}环境功能验证")
# 动态设置严重程度
if env == "prod":
allure.dynamic.severity(allure.severity_level.CRITICAL)
else:
allure.dynamic.severity(allure.severity_level.NORMAL)
# 执行测试逻辑
pass2. 条件性附件
import allure
import os
def test_with_conditional_attachments():
"""条件性附件测试"""
debug_mode = os.getenv("DEBUG", "false").lower() == "true"
with allure.step("执行业务逻辑"):
result = some_business_logic()
# 只在调试模式下附加详细信息
if debug_mode:
AllureAttachmentHelper.attach_json(
{"debug_info": "详细调试信息", "variables": locals()},
"调试信息"
)
with allure.step("验证结果"):
assert result is not None
# 失败时自动附加错误信息
if not result:
AllureAttachmentHelper.attach_text(
"测试失败,请检查业务逻辑",
"错误信息"
)报告定制与美化
1. 环境信息配置
创建environment.properties文件:
# environment.properties
测试环境=开发环境
服务器地址=https://dev-api.example.com
数据库=MySQL 8.0
Python版本=3.9.7
操作系统=macOS 12.6
浏览器=Chrome 105.0
测试执行人=张三
执行时间=2023-10-01 10:00:002. 自定义分类
创建categories.json文件:
[
{
"name": "产品缺陷",
"matchedStatuses": ["failed"],
"messageRegex": ".*AssertionError.*"
},
{
"name": "测试数据问题",
"matchedStatuses": ["failed"],
"messageRegex": ".*数据.*|.*data.*"
},
{
"name": "环境问题",
"matchedStatuses": ["broken"],
"messageRegex": ".*ConnectionError.*|.*timeout.*"
},
{
"name": "测试脚本问题",
"matchedStatuses": ["broken"],
"messageRegex": ".*AttributeError.*|.*NameError.*"
}
]3. 报告主题定制
# conftest.py
import allure
import pytest
import os
from datetime import datetime
@pytest.fixture(scope="session", autouse=True)
def setup_allure_environment():
"""设置allure环境信息"""
# 动态生成环境信息
env_info = {
"测试环境": os.getenv("TEST_ENV", "dev"),
"服务器地址": os.getenv("API_BASE_URL", "https://dev-api.example.com"),
"Python版本": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
"操作系统": platform.system(),
"执行时间": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"执行人": os.getenv("USER", "未知")
}
# 写入环境信息文件
allure_results_dir = "allure-results"
os.makedirs(allure_results_dir, exist_ok=True)
with open(f"{allure_results_dir}/environment.properties", "w", encoding="utf-8") as f:
for key, value in env_info.items():
f.write(f"{key}={value}\n")
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""测试报告钩子"""
outcome = yield
rep = outcome.get_result()
# 失败时自动截图(如果是UI测试)
if rep.when == "call" and rep.failed:
if hasattr(item, "funcargs") and "driver" in item.funcargs:
# UI测试失败截图
driver = item.funcargs["driver"]
screenshot = driver.get_screenshot_as_png()
allure.attach(
screenshot,
name="失败截图",
attachment_type=allure.attachment_type.PNG
)
# 附加失败信息
allure.attach(
str(rep.longrepr),
name="失败详情",
attachment_type=allure.attachment_type.TEXT
)高级特性与技巧
1. 测试套件组织
import allure
@allure.epic("电商平台")
class TestEcommercePlatform:
"""电商平台测试套件"""
@allure.feature("用户管理")
class TestUserManagement:
"""用户管理测试"""
@allure.story("用户注册")
@allure.severity(allure.severity_level.CRITICAL)
def test_user_registration(self):
"""用户注册测试"""
pass
@allure.story("用户登录")
@allure.severity(allure.severity_level.CRITICAL)
def test_user_login(self):
"""用户登录测试"""
pass
@allure.feature("商品管理")
class TestProductManagement:
"""商品管理测试"""
@allure.story("商品创建")
@allure.severity(allure.severity_level.NORMAL)
def test_product_creation(self):
"""商品创建测试"""
pass
@allure.story("商品搜索")
@allure.severity(allure.severity_level.NORMAL)
def test_product_search(self):
"""商品搜索测试"""
pass2. 性能测试集成
import allure
import time
import psutil
@allure.feature("性能测试")
class TestPerformance:
"""性能测试套件"""
def test_api_response_time(self):
"""API响应时间测试"""
with allure.step("记录开始时间"):
start_time = time.time()
start_memory = psutil.virtual_memory().used
with allure.step("执行API调用"):
response = requests.get("https://api.example.com/data")
with allure.step("计算性能指标"):
end_time = time.time()
end_memory = psutil.virtual_memory().used
response_time = end_time - start_time
memory_usage = end_memory - start_memory
with allure.step("附加性能数据"):
performance_data = {
"响应时间": f"{response_time:.3f}秒",
"内存使用": f"{memory_usage / 1024 / 1024:.2f}MB",
"状态码": response.status_code,
"响应大小": f"{len(response.content)}字节"
}
AllureAttachmentHelper.attach_json(performance_data, "性能指标")
# 性能断言
assert response_time < 2.0, f"响应时间{response_time:.3f}秒超过2秒限制"
assert response.status_code == 2003. 历史趋势分析
# 生成带历史数据的报告
allure generate allure-results -o allure-report --clean
# 保留历史数据
cp -r allure-report/history allure-results/
# 下次生成报告时会包含历史趋势
allure generate allure-results -o allure-report --clean报告发布与分享
1. CI/CD集成
# .github/workflows/test.yml
name: 自动化测试
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: 设置Python环境
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: 安装依赖
run: |
pip install -r requirements.txt
- name: 运行测试
run: |
pytest --alluredir=allure-results
- name: 生成Allure报告
uses: simple-elf/allure-report-action@master
if: always()
with:
allure_results: allure-results
allure_report: allure-report
gh_pages: allure-history
- name: 发布报告到GitHub Pages
uses: peaceiris/actions-gh-pages@v2
if: always()
env:
PERSONAL_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PUBLISH_BRANCH: gh-pages
PUBLISH_DIR: allure-history2. 邮件报告
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import zipfile
import os
class AllureReportSender:
"""Allure报告发送器"""
def __init__(self, smtp_server, smtp_port, username, password):
self.smtp_server = smtp_server
self.smtp_port = smtp_port
self.username = username
self.password = password
def send_report(self, report_dir, recipients, subject="自动化测试报告"):
"""发送测试报告"""
# 压缩报告文件
zip_path = "allure-report.zip"
with zipfile.ZipFile(zip_path, 'w') as zipf:
for root, dirs, files in os.walk(report_dir):
for file in files:
zipf.write(os.path.join(root, file))
# 创建邮件
msg = MIMEMultipart()
msg['From'] = self.username
msg['To'] = ", ".join(recipients)
msg['Subject'] = subject
# 邮件正文
body = """
亲爱的同事,
请查收最新的自动化测试报告。
报告包含了详细的测试结果、执行步骤和性能数据。
祝好!
自动化测试团队
"""
msg.attach(MIMEText(body, 'plain', 'utf-8'))
# 附加报告文件
with open(zip_path, "rb") as attachment:
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
'Content-Disposition',
f'attachment; filename= {zip_path}'
)
msg.attach(part)
# 发送邮件
server = smtplib.SMTP(self.smtp_server, self.smtp_port)
server.starttls()
server.login(self.username, self.password)
text = msg.as_string()
server.sendmail(self.username, recipients, text)
server.quit()
# 清理临时文件
os.remove(zip_path)
# 使用示例
def send_test_report():
"""发送测试报告"""
sender = AllureReportSender(
smtp_server="smtp.company.com",
smtp_port=587,
username="test@company.com",
password="password"
)
recipients = ["manager@company.com", "team@company.com"]
sender.send_report("allure-report", recipients)总结
allure不仅仅是一个测试报告工具,更是提升测试团队专业形象的利器。通过合理使用allure的各种特性,我们可以生成既美观又实用的测试报告。
关键要点回顾:
- 分层组织:使用epic、feature、story构建清晰的测试层次
- 丰富信息:通过装饰器和附件提供详细的测试信息
- 动态内容:根据测试情况动态设置标题、描述等
- 定制化:通过配置文件定制报告外观和分类
- 集成发布:与CI/CD流程集成,自动生成和发布报告
下一篇文章,我们将深入探讨接口自动化框架的架构设计,敬请期待!
