Allure 是一个灵活且可扩展的测试报告工具,会生成详细的测试报告,可以与 pytest 测试框架集成,输出 html 格式的测试报告。它的使用方法比较简单,这里大致说一下。

Allure 报告的主要特点包括:

  • 丰富的图表:提供测试结果的概览,如通过率、失败率等。
  • 详细的时间轴:显示每个测试用例的执行时间和状态。
  • 步骤详情:展示测试用例中的每一个步骤,便于追踪问题。
  • 附件支持:可以附加屏幕截图、日志文件等,有助于问题复现。
  • 标签和分类:通过标签对测试用例进行分类,方便筛选和查询。

安装 JDK

Allure 需要 JAVA 环境才可以使用,我们先要安装一下 JDK。

点击JDK下载地址,可以下载最新版本(.zip),如果实际使用中发现和 Allure 版本不兼容,就升级 Allure 版本,或下载 JDK 低版本。

下载后,解压,再移动 jdk-XX.X.X 目录到指定的目录下,记住这个目录。

不要随意改动环境变量,否则可能造成系统死机。

JDK 需要配置环境变量,步骤如下:

配置JDK系统变量

  • 右键此电脑-属性-高级系统设置-环境变量

  • 系统变量-点击新建

  • 变量名:JAVA_HOME

  • 变量值:JDK 放置目录(比如:C:\Program1\Java\jdk-24.0.1)

配置JDK的环境环境

  • 环境变量中找到Path,选中后点击编辑

  • 新建环境变量:%JAVA_HOME%\bin;

  • 新建环境变量:%JAVA_HOME%\jre\bin;

验证 JAVA 环境变量

  • win + r,输入 cmd

  • 命令行中执行:java -version

  • 输出类似 java version "24.0.1" 2025-04-15,说明配置成功

部署 Allure

访问 Allure 下载最新包(allure-2.xx.x.zip),解压并重命名 allure 后粘贴至项目根目录下。结构如下:

Project/
├── allure/
│   ├── bin/
│   ├── config/
│   ├── lib/
│   └── plugins/ 
├── Config/
│	└── pytest.ini            # 自定义 mark 标记
├── ParametrizeData/
│   ├── user_add.json         # user_add测试用例的参数化数据
│   └── user_delete.json      # user_delete测试用例的参数化数据
├── Package/                  # 程序目录
│   ├── __init__.py           # 包初始化文件,可以定义一些变量或执行一些操作。当然里面什么都不写也可以。
│   ├── timeout.py            # 工具模块,定义了超时标记
│   ├── tools.py              # 工具模块,包括处理参数化文件逻辑
│   ├── module1.py            # 测试程序模块,比如连接数据库操作数据,接口请求等操作,推荐按功能封装成类
│   └── module2.py            # 测试程序模块,比如连接数据库操作数据,接口请求等操作,推荐按功能封装成类
├── Test/                     # 测试用例目录
│   ├── __init__.py           # 包初始化文件
│   ├── test_module1.py       # 测试 module1 的测试用例
│   └── test_module2.py       # 测试 module2 的测试用例
├── app.py                    # 项目启动文件
├── requirements.txt          # 项目依赖项列表
└── README.md                 # 项目说明文档

安装 allure-pytest :

pip install allure-pytest

编辑 app.py ,支持运行 allure 可执行文件:

import subprocess
import pytest
import sys
import logging
import argparse
import os

# 自定义日志格式
LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s'
logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
logger = logging.getLogger(__name__)
# 配置文件
CONFIG_PATH = "Config/pytest.ini"

def run_tests(test_target: str, testprint: str, tb: str, mark: str)->int:
    """
    运行pytest测试并返回退出码

    Args:
        test_target (str): 测试目标路径
        testprint (str): 控制输出信息的命令行参数
        tb (str): 控制输出信息的命令行参数
        mark (str): 指定运行特定标记的测试用例

    Returns:
        int: pytest退出码
    """
    if not os.path.exists(test_target):
        logger.error(f"测试目标路径不存在: {test_target}")
        return 1

    # 构建pytest参数
    pytest_args = []
    if os.path.exists(CONFIG_PATH):
        pytest_args.append("-c")
        pytest_args.append(CONFIG_PATH)
    else:
        logger.warning("未找到配置文件,将使用默认配置")
    if testprint:
        pytest_args.append("-"+ testprint)
    pytest_args.extend([
        test_target,
        "--clean-alluredir", "--alluredir=./result",
        "--tb=" + tb
    ])
    if mark:
        pytest_args.append("-m")
        pytest_args.append(mark)


    try:
        logger.info(f"开始运行测试,目标路径: {test_target}")
        exit_code = pytest.main(pytest_args)

        # 退出码说明映射
        exit_messages = {
            0: "✅ 全部测试用例通过",
            1: "⚠️ 部分测试用例未通过",
            2: "❌ 测试过程中有中断或其他非正常终止",
            3: "❌ 内部错误",
            4: "❌ pytest无法找到任何测试用例",
            5: "❌ pytest遇到了命令行解析错误"
        }

        logger.info(exit_messages.get(exit_code, f"❓ 未知的退出码: {exit_code}"))
        return exit_code

    except Exception as e:
        logger.exception("运行测试时发生致命错误:")
        logger.debug("异常详情:", exc_info=True)
        return 1


def parse_arguments()-> argparse.Namespace:
    """解析命令行参数"""
    parser = argparse.ArgumentParser(description="使用指定的命令运行 pytest 测试")
    parser.add_argument(
        'test_target',
        nargs='?',
        type=str,
        default="Test/",
        help='指定测试目录文件 (默认: Test/)'
    )
    parser.add_argument(
        '-p', '--testprint',
        nargs='?',
        type=str,
        default="",
        choices=["","v", "ra", "rA", "rp", "rx", "rs", "rN", "vra", "vrA", "vrp", "vrx", "vrs", "vrN"],
        help='控制输出信息'
    )
    parser.add_argument(
        '-tb', '--tb',
        nargs='?',
        type=str,
        default="auto",
        choices=["auto","long", "short", "line", "native", "no"],
        help='控制输出信息'
    )
    parser.add_argument(
        '-m', '--mark',
        nargs='?',
        type=str,
        help='控制输出信息'
    )
    return parser.parse_args()


if __name__ == "__main__":
    args = parse_arguments()

    exit_code = run_tests(args.test_target, args.testprint, args.tb, args.mark)
    # 生成 Allure 报告
    try:
        allure_command = [
            r".\allure\bin\allure.bat", "generate",
            "./result",
            "-o", "./report",
            "--clean"
        ]
        result = subprocess.run(allure_command, check=True, text=True, capture_output=True, cwd=os.getcwd())
        logging.info("Allure 报告生成成功")
        logging.info(result.stdout)
    except subprocess.CalledProcessError as e:
        logging.error(f"生成 Allure 报告时出错: {e}")
        logging.error(e.stderr)

    sys.exit(exit_code)

使用 allure

allure 支持自定义设置,包括:

  • @allure.epic():用于标记测软件系统名称,例如:XX管理系统。

  • @allure.feature():用于标记被测功能模块,例如:用户管理模块。

  • @allure.story():用于标记被测功能点或场景,例如:删除用户。

  • @allure.title():用于标记测试用例的标题,例如:冒烟测试-删除用户。

  • @allure.description():用于为测试用例添加描述信息,例如:验证是否可以删除用户。

  • @allure.tag():用于给测试用例添加标签。这些标签可以用来分类测试用例,便于在生成的测试报告中进行筛选和组织。例如:冒烟测试。

  • @allure.severity():用于指定测试用例的重要性级别,由高到低分为 blocker、critical、normal、minor、trivial 五级。

  • @pytest.allure.step():用于标记测试步骤。

假设书籍管理系统有用户管理模块,系统管理员登录系统后可以新增、编辑、删除用户。使用 pytest + allure 构建测试:

Package 目录下新建 user.py ,编写需要用到的函数。比如登录、获取用户列表、删除用户和查询数据库用户数据:

import allure

@allure.step("登录获取token")
def get_token(user_name, password):
    if user_name == "JZY" and password == "123456":
        # 此处省略登录的代码,直接返回token
        return True, "token"
    return False, "具体的错误原因"

@allure.step("获取用户列表")
def get_user_list(token):
    if token != "token":
        return False, "用户未登录"
    # 此处省略获取用户列表的代码,直接返回用户列表,应该有异常处理,比如请求地址错误等
    return True, [{"username": "test_user", "user_id":1},{"username": "test_user1", "user_id":2}]


@allure.step("删除用户")
def delete_user(token, user_id):
    if token != "token" or  not user_id:
        return False, "token错误"
    # 此处省略删除用户的代码,直接返回删除结果
    return True, "删除成功"

@allure.step("查询数据库中用户数据")
def select_user():
    # 此处省略查询数据库中用户数据的代码,直接返回查询结果
    return True, [{"username": "test_user1", "user_id":2}]

Test 目录下新建 test_user.py ,编写测试用例:

import allure

from Package.user import get_token, get_user_list, delete_user


@allure.epic("书籍管理平台接口测试")
@allure.feature("用户管理模块测试")
class TestUserManagement:

    @allure.story("新增用户测试")
    @allure.title("新增用户-冒烟")
    @allure.description("验证系统可以新增用户")
    @allure.severity("blocker")
    def test_add_user_smoke(self):
        pass

    @allure.story("新增用户测试")
    @allure.title("新增用户-正向测试")
    @allure.description("验证系统可以正确新增符合条件的用户")
    @allure.severity("critical")
    def test_add_user_success(self):
        pass

    @allure.story("新增用户测试")
    @allure.title("新增用户-异向测试")
    @allure.description("验证系统可以正确处理新增用户的异常参数,包括sql注入、边界值等")
    @allure.severity("critical")
    def test_add_user_fail(self):
        pass

    @allure.story("删除用户测试")
    @allure.title("删除用户-冒烟")
    @allure.description("验证系统可以删除用户")
    @allure.severity("blocker")
    def test_delete_user_smoke(self):
        result, user_token = get_token("JZY", "123456")
        if not result:
            assert False, "用户登录失败,速检查原因"
        result, user_list = get_user_list(user_token)
        if not result:
            assert False, user_list
        result, msg = delete_user(user_token, user_list[0]["user_id"])
        assert result, msg

运行结果:

result 目录下是产生的测试数据,report 目录下的 index.html 是我们最终的目标。在浏览器打开 index.html :

allure 使用参数化数据

allure 还可以应用于参数化测试中,比如:

import allure
import pytest

# 定义测试数据
test_data = [
    ("登录账号 - 正确密码", "admin", "123456"),
    ("登录账号 - 错误密码", "admin", "wrongpassword"),
    ("登录账号 - 错误账号", "adminerror", "123456"),
    ("登录账号 - 空密码", "admin", "")
]


# 参数化测试
@pytest.mark.parametrize("title, user_name, password", test_data, ids=[data[0] for data in test_data])
@allure.story("测试参数化")
@allure.severity("blocker")
def test_login(title, user_name, password):
    # 动态设置测试用例标题
    allure.dynamic.title(title)
    # 动态设置测试用例描述
    allure.dynamic.description(f"用户名: {user_name}, 密码: {password}")

    # 模拟登录操作
    with allure.step("执行登录操作"):
        # 打印的日志会被allure记录
        print(f"尝试使用用户名 '{user_name}' 和密码 '{password}' 登录")

        # 假设这是一个成功的登录
        assert True, "登录失败"

运行结果:

为测试结果添加附件

有两种方式添加附件:

  • allure.attach(body, name=None, attachment_type="text/plain", extension="attach")
  • allure.attach.file(source, name=None, attachment_type=None, extension=None)

allure.attach() 的参数中,要求 body 的值必须是 bytes或str 类型,name 为此附件的名称(可作附件说明),attachment_type 指定数据的类型(默认文本就行,添加文件附件推荐使用 allure.attach.file() ),extension 不必理会。

allure.attach.file() 的参数中,source 指定文件的路径,name 为此附件的名称(可作附件说明),attachment_type 指定数据的类型,extension 不必理会。

例子:

import allure

@allure.story("添加附件测试")
@allure.severity("blocker")
def test_text():
    # 添加附件文本
    allure.attach(
        body="这是 test_login 的文本附件",
        name="文本附件说明",
        attachment_type=allure.attachment_type.TEXT
    )
    assert True, "登录失败"

@allure.story("添加附件测试")
@allure.severity("blocker")
def test_files():
    # 添加附件文本
    allure.attach.file(
        source="Test/img.png",  # app.py的相对路径
        name="文件附件说明",
        attachment_type=allure.attachment_type.PNG
    )
    assert True, "登录失败"

运行结果:


THEEND



© 转载需要保留原始链接,未经明确许可,禁止商业使用。CC BY-NC-ND 4.0