pytest 参数化测试:同一用例跑多组数据,数据驱动就这么简单

小飞兽 Python 9 次阅读 2026-07-24

Introduction

测试登录功能,你需要验证:

  • 用户名正确 + 密码正确 → 登录成功

  • 用户名正确 + 密码错误 → 登录失败

  • 用户名为空 → 验证失败

  • 用户不存在 → 登录失败

没有参数化?你需要写 4 个几乎一样的测试函数。用 @pytest.mark.parametrize,一行代码搞定。

---

基础语法

1. 单参数

import pytest

@pytest.mark.parametrize("username", ["admin", "test", "guest"])
def test_username_format(username):
    assert len(username) >= 3
    assert username.isalnum()

运行结果:3个测试用例分别执行。

2. 多参数组合

@pytest.mark.parametrize("username,password,expected", [
    ("admin", "123456", True),
    ("admin", "wrong", False),
    ("", "123456", False),
    ("nobody", "anypass", False),
])
def test_login(username, password, expected):
    result = login(username, password)
    assert result == expected

pytest 自动生成 4 个测试组合,覆盖所有场景。

3. 与 Fixtures 配合

@pytest.fixture
def api_client():
    return APIClient(base_url="https://api.example.com")

@pytest.mark.parametrize("endpoint,status_code", [
    ("/users", 200),
    ("/orders", 200),
    ("/products", 200),
])
def test_api_endpoints(api_client, endpoint, status_code):
    response = api_client.get(endpoint)
    assert response.status_code == status_code

4. 反向参数(ids)

给每个测试用例起一个可读的名字:

@pytest.mark.parametrize("username,password,expected", [
    ("admin", "123456", True),
    ("admin", "wrong", False),
], ids=["valid_creds", "invalid_password"])
def test_login(username, password, expected):
    ...

运行输出会显示 test_login[valid_creds] 而不是 test_login[0]

5. 间接参数(indirect)

把参数传给 fixture 而不是直接传给测试函数:

@pytest.fixture
def db_connection(db_name):
    return Database(db_name)

@pytest.mark.parametrize("db_name", ["test_db", "prod_db"], indirect=["db_name"])
def test_database_query(db_connection, db_name):
    result = db_connection.query("SELECT 1")
    assert result is not None

---

运行效果

$ pytest test_parametrize.py -v

test_parametrize.py::test_login[valid_creds] PASSED       [ 25%]
test_parametrize.py::test_login[invalid_password] PASSED   [ 25%]
test_parametrize.py::test_username_format[admin] PASSED   [ 50%]
test_parametrize.py::test_username_format[test] PASSED    [ 75%]
test_parametrize.py::test_username_format[guest] PASSED    [100%]

---

常见问题

Q1: 只想跑其中某组参数?

pytest -k "valid_creds"

Q2: 全部参数都失败才报错?

不是,pytest 独立运行每组参数,一组失败不影响其他组。

Q3: 参数太多写不下?

可以用外部文件(CSV/JSON)存储测试数据,然后用 pytest_generate_tests 动态加载:
import pytest
import json

def pytest_generate_tests(metafunc):
    if "test_data" in metafunc.fixturenames:
        with open("test_data.json") as f:
            data = json.load(f)
        metafunc.parametrize("test_data", data)

---

延伸阅读

  • [[pytest Fixtures 详解]] — 测试夹具的创建与作用域
  • [[pytest 插件生态]] — 覆盖率报告、并行执行、HTML报告
  • [[Selenium + pytest 集成]] — 数据驱动的浏览器自动化测试

---
有问题欢迎在评论区交流!