pytest冒烟测试用例过滤执行实例

如何在庞大的全量测试集中,精准定位并执行核心链路,而无需重构整个测试生态。

实战方案设计

核心思路:方案一提供物理隔离的目录结构,适合稳定版本快速验证;方案二提供动态优先级过滤,支持按需灵活调度。两者结合,可以覆盖大多数业务场景。

实例结构

复制代码
test_project/
├── conftest.py                # pytest钩子配置
├── pytest.ini                 # pytest配置文件
├── requirements.txt           # 依赖包
├── test_smoke/                # 方案一:冒烟用例独立目录
│   ├── test_login.py
│   └── test_checkout.py
├── test_full/                 # 方案一:全量用例目录
│   ├── test_login.py
│   ├── test_checkout.py
│   ├── test_profile.py
│   └── test_orders.py
├── test_cases/                # 方案二:YAML用例集中管理
│   ├── login.yaml
│   ├── checkout.yaml
│   └── profile.yaml
└── run_tests.py               # 方案二:自定义命令行参数处理

核心代码实现

1. pytest.ini(pytest基础配置)

复制代码
[pytest]
testpaths = .
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --tb=short
markers =
    smoke: 冒烟测试标记
    blocker: 高优先级阻塞问题
    critical: 关键优先级问题
    normal: 普通优先级问题

2. conftest.py(pytest钩子与自定义参数)

python 复制代码
import pytest
from typing import List, Dict
import yaml
import os

def pytest_addoption(parser):
    """自定义命令行参数"""
    parser.addoption("--level", action="store", default=None,
                    help="指定用例优先级: blocker,critical,normal")
    parser.addoption("--cases", action="store", default=None,
                    help="指定用例目录路径")
    parser.addoption("--type", action="store", default=None,
                    help="指定用例类型: yaml, python")

def pytest_configure(config):
    """动态注册marker"""
    config.addinivalue_line("markers", "smoke: 冒烟测试")
    config.addinivalue_line("markers", "blocker: 阻塞级别")
    config.addinivalue_line("markers", "critical: 关键级别")
    config.addinivalue_line("markers", "normal: 普通级别")

def pytest_collection_modifyitems(config, items):
    """动态过滤用例(方案二实现)"""
    level = config.getoption("--level")
    if not level:
        return
    
    levels = level.split(",")
    selected_items = []
    
    for item in item:
        # 获取用例的所有marker
        markers = {mark.name for mark in item.iter_markers()}
        
        # 检查是否匹配指定优先级
        if any(lvl in markers for lvl in levels):
            selected_items.append(item)
    
    items[:] = selected_items
    if selected_items:
        config.hook.pytest_deselected(items=items)

3. run_tests.py(方案二的YAML解析器)

python 复制代码
import pytest
import yaml
import os
from typing import List, Dict

def case_parser(case_type: str, case_dir: str, level: str = None) -> Dict:
    """
    YAML用例解析器(方案二核心逻辑)
    
    Args:
        case_type: 用例类型,如 'yaml'
        case_dir: 用例目录路径
        level: 优先级过滤条件
    
    Returns:
        包含过滤后用例信息的字典
    """
    case_infos = []
    
    if case_type != "yaml":
        return {"case_infos": case_infos}
    
    # 遍历目录获取所有YAML文件
    yaml_files = []
    for root, _, files in os.walk(case_dir):
        for file in files:
            if file.endswith(('.yaml', '.yml')):
                yaml_files.append(os.path.join(root, file))
    
    # 解析YAML用例
    yaml_case_infos = []
    for yaml_file in yaml_files:
        with open(yaml_file, 'r', encoding='utf-8') as f:
            data = yaml.safe_load(f)
            if isinstance(data, list):
                yaml_case_infos.extend(data)
            else:
                yaml_case_infos.append(data)
    
    # 根据优先级过滤(方案二关键逻辑)
    if level:
        levels = level.split(",")
        for case_info in yaml_case_infos:
            if case_info.get("rank") in levels:
                case_infos.append(case_info)
    else:
        case_infos = yaml_case_infos
    
    return {"case_infos": case_infos}

def pytest_generate_tests(metafunc):
    """动态生成测试用例(YAML用例注入)"""
    if "case_info" in metafunc.fixturenames:
        case_dir = metafunc.config.getoption("--cases")
        case_type = metafunc.config.getoption("--type")
        level = metafunc.config.getoption("--level")
        
        if case_dir and case_type:
            result = case_parser(case_type, case_dir, level)
            metafunc.parametrize("case_info", result["case_infos"])

4. 方案一:目录分离的测试用例

test_smoke/test_login.py

python 复制代码
import pytest

@pytest.mark.smoke
def test_login_success():
    """冒烟测试:登录成功"""
    print("执行冒烟测试:登录成功")
    assert True

@pytest.mark.smoke
def test_login_with_valid_credentials():
    """冒烟测试:有效凭证登录"""
    print("执行冒烟测试:有效凭证登录")
    assert True

test_smoke/test_checkout.py

python 复制代码
import pytest

@pytest.mark.smoke
@pytest.mark.blocker
def test_checkout_basic_flow():
    """冒烟测试:基础购物流程"""
    print("执行冒烟测试:基础购物流程")
    assert True

test_full/test_login.py(包含更多边界场景)

python 复制代码
import pytest

@pytest.mark.smoke
def test_login_success():
    """全量测试:登录成功"""
    assert True

@pytest.mark.normal
def test_login_with_invalid_password():
    """全量测试:错误密码登录"""
    assert True

@pytest.mark.normal
def test_login_with_empty_fields():
    """全量测试:空字段登录"""
    assert True

@pytest.mark.critical
def test_login_with_locked_account():
    """全量测试:锁定账号登录"""
    assert True

5. 方案二:YAML用例文件

test_cases/login.yaml

python 复制代码
- desc: 登录用例-正常登录
  rank: blocker
  steps:
    - step: 打开登录页面
      expected: 页面加载成功
    - step: 输入用户名密码
      expected: 输入成功
    - step: 点击登录按钮
      expected: 登录成功跳转

- desc: 登录用例-错误密码
  rank: normal
  steps:
    - step: 打开登录页面
      expected: 页面加载成功
    - step: 输入用户名和错误密码
      expected: 输入成功
    - step: 点击登录按钮
      expected: 提示密码错误

- desc: 登录用例-锁定账号
  rank: critical
  steps:
    - step: 打开登录页面
      expected: 页面加载成功
    - step: 输入锁定账号
      expected: 输入成功
    - step: 点击登录按钮
      expected: 提示账号已锁定

test_cases/checkout.yaml

复制代码
- desc: 购物流程-基础流程
  rank: blocker
  steps:
    - step: 选择商品
      expected: 商品选中
    - step: 加入购物车
      expected: 购物车数量增加
    - step: 提交订单
      expected: 订单提交成功

- desc: 购物流程-库存不足
  rank: critical
  steps:
    - step: 选择库存不足商品
      expected: 商品选中
    - step: 加入购物车
      expected: 提示库存不足

6. YAML用例驱动测试(方案二执行器)

test_yaml_runner.py

python 复制代码
import pytest

def test_yaml_case(case_info):
    """
    YAML用例通用执行器
    通过conftest.py中的pytest_generate_tests动态注入用例
    """
    print(f"\n执行用例:{case_info['desc']}")
    print(f"优先级:{case_info.get('rank', '无')}")
    
    for idx, step in enumerate(case_info['steps'], 1):
        print(f"步骤{idx}: {step['step']}")
        print(f"预期: {step['expected']}")
        # 这里可以添加实际的测试逻辑
        # 例如调用API、操作页面等
    
    assert True  # 简化示例,实际应根据步骤执行结果断言

7. requirements.txt

复制代码
pytest>=7.0.0
pyyaml>=6.0
pytest-html>=3.0.0

执行示例与命令

方案一:目录分离模式执行

复制代码
# 只执行冒烟测试(指定smoke目录)
pytest test_smoke/ -v

# 执行全量测试(指定full目录)
pytest test_full/ -v

# 使用自定义参数执行冒烟测试
pytest --cases=./test_smoke --type=python -v

方案二:优先级过滤模式执行

复制代码
# 只执行blocker和critical级别用例(冒烟测试)
pytest --level=blocker,critical -v

# 执行指定级别用例
pytest --level=normal -v

# 结合YAML用例执行
pytest test_yaml_runner.py --cases=./test_cases --type=yaml --level=blocker,critical -v

组合场景执行

复制代码
# 执行冒烟目录 + blocker级别用例
pytest test_smoke/ --level=blocker -v

# 并行执行多个测试集合
pytest test_smoke/ test_full/ --level=blocker,critical -v

实例亮点与最佳实践体现

  1. 方案一体现

    • test_smoke/test_full/ 物理分离,结构清晰
    • 通过 pytest 命令直接指定目录执行,简单高效
    • 适合项目稳定后,冒烟用例不常变化的场景
  2. 方案二体现

    • YAML 用例统一管理,便于维护和版本控制
    • 通过 --level 参数动态过滤,支持灵活调度
    • 适合用例频繁调整、需要按优先级分批执行的场景
  3. 两者结合

    • 目录分离处理核心稳定链路(如登录、支付)
    • YAML + 优先级处理需要动态调整的业务场景(如营销活动)
    • 通过同一个配置体系(pytest.ini + conftest.py)统一管理
  4. 工程化细节

    • pytest 钩子实现动态过滤,无需修改测试代码
    • Marker 体系标准化用例分类
    • YAML 解析器支持优先级字段过滤,完全复现你提供的逻辑
相关推荐
love530love2 小时前
Windows 多 Git 环境冲突:一个环境变量优先级引发的血案
人工智能·windows·git·环境变量·scoop
pursue.dreams2 小时前
Windows 安装 RabbitMQ 保姆级教程
windows·rabbitmq
Zhu_S W2 小时前
EasyExcel动态表头详解
java·linux·windows
x***r15115 小时前
Zotero7.0.8 文献管理安装步骤详解(附文献管理与同步设置教程)
windows
何中应17 小时前
解决Windows CMD中文乱码问题
windows·操作系统
何中应17 小时前
Windows打开命令窗口的几种方式
windows·操作系统
水饺编程21 小时前
第4章,[标签 Win32] :TextOut 测试案例2
c语言·c++·windows·visual studio
i建模21 小时前
Omarchy挂载windows磁盘
linux·运维·windows
A懿轩A1 天前
【Java 基础编程】Java 集合框架详解:List/Set/Map 选型 + ArrayList/HashMap 原理与使用
java·windows·list