Pytest 是 Python 社区广泛使用的测试框架,提供了灵活的功能来提升测试覆盖率和代码质量。本文将详细介绍 Pytest 的 参数化 和 基本装饰器用法,帮助你在编写测试用例时更加高效和灵活。
一、Pytest 参数化(Parametrize)
1. 什么是参数化?
参数化是指在一个测试用例中使用多组不同的数据,避免重复编写类似的测试代码,从而提升测试效率。
2. 参数化的基本语法
Pytest 提供了 @pytest.mark.parametrize
装饰器来实现参数化测试。
语法
@pytest.mark.parametrize("参数名1, 参数名2, ...", [(值1, 值2, ...), ...])
def 测试函数(参数名1, 参数名2, ...):
# 测试逻辑
复制代码
3. 单参数的参数化
测试单一参数的多种情况:
import pytest
@pytest.mark.parametrize("number", [1, 2, 3, 4])
def test_is_positive(number):
assert number > 0
复制代码
执行结果:
test_example.py::test_is_positive[1] PASSED
test_example.py::test_is_positive[2] PASSED
test_example.py::test_is_positive[3] PASSED
test_example.py::test_is_positive[4] PASSED
复制代码
4. 多参数的参数化
当测试函数需要多个参数时,可以提供参数组合:
import pytest
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(2, 3, 5),
(3, 5, 8),
])
def test_addition(a, b, expected):
assert a + b == expected
复制代码
执行结果:
test_example.py::test_addition[1-2-3] PASSED
test_example.py::test_addition[2-3-5] PASSED
test_example.py::test_addition[3-5-8] PASSED
复制代码
通过 @pytest.mark.parametrize
的多重装饰实现笛卡尔积的参数组合:
import pytest
@pytest.mark.parametrize("a", [1, 2])
@pytest.mark.parametrize("b", [3, 4])
def test_combinations(a, b):
print(f"Testing combination: {a}, {b}")
assert a + b in [4, 5, 6]
复制代码
生成的组合:
6. 参数化数据来源:读取外部数据
从 JSON 文件读取
import pytest
import json
def load_test_data():
with open("test_data.json", "r") as file:
return json.load(file)
@pytest.mark.parametrize("a, b, expected", load_test_data())
def test_from_json(a, b, expected):
assert a + b == expected
复制代码
从 CSV 文件读取
import pytest
import csv
def load_csv(file_path):
with open(file_path, "r") as file:
return [tuple(row) for row in csv.reader(file)]
@pytest.mark.parametrize("a, b, expected", load_csv("test_data.csv"))
def test_from_csv(a, b, expected):
assert int(a) + int(b) == int(expected)
复制代码
二、Pytest 基本装饰器用法
Pytest 提供了多种装饰器,帮助控制测试的执行行为,例如 跳过测试、预期失败、条件跳过 和 自定义标记。
1. 跳过测试
直接跳过
如果某些测试暂时不需要执行,可以使用 @pytest.mark.skip
:
import pytest
@pytest.mark.skip(reason="Feature not implemented")
def test_not_ready():
assert 1 + 1 == 2
复制代码
条件跳过
根据条件决定是否跳过测试:
import pytest
import sys
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Requires Python 3.8+")
def test_python_version():
assert 1 + 1 == 2
复制代码
2. 标记预期失败
如果某个功能尚未完善,测试会失败,但我们希望忽略失败结果,可以使用 @pytest.mark.xfail
:
import pytest
@pytest.mark.xfail(reason="Known bug")
def test_known_bug():
assert 1 + 1 == 3
复制代码
运行结果会显示 XFAIL
。
3. 自定义标记
标记是 Pytest 用于分组和筛选测试用例的强大工具。使用 @pytest.mark
为测试用例添加自定义标记:
import pytest
@pytest.mark.smoke
def test_quick_check():
assert 1 + 1 == 2
@pytest.mark.regression
def test_full_check():
assert 2 * 3 == 6
复制代码
运行特定分组测试用例:
4. 自定义装饰器与作用范围
Fixture 的作用范围
function
(默认):每个测试函数调用一次。
module
:在模块范围内共享。
class
:在类范围内共享。
session
:在会话范围内共享。
示例:
import pytest
@pytest.fixture(scope="module")
def setup_module():
print("Setting up module")
return "Resource"
def test_resource_1(setup_module):
assert setup_module == "Resource"
def test_resource_2(setup_module):
assert setup_module == "Resource"
复制代码
5. 参数化与装饰器结合
参数化可以与其他装饰器组合使用,例如条件跳过:
import pytest
@pytest.mark.parametrize("input, expected", [(1, 2), (3, 4)])
@pytest.mark.skipif(condition=True, reason="Skipped for demonstration")
def test_skip_with_parametrize(input, expected):
assert input + 1 == expected
复制代码
充分利用参数化:
避免硬编码,提升测试覆盖率。
使用外部文件存储测试数据,增强数据管理能力。
灵活使用装饰器:
跳过和标记功能可以帮助测试流程更高效。
使用条件跳过和预期失败,优化测试策略。
与 CI/CD 集成:
维护清晰的日志和报告:
Pytest 的参数化和装饰器功能为自动化测试提供了强大的灵活性和可扩展性。通过合理使用这些功能,可以显著提升测试用例的质量和可维护性。如果你有更多关于 Pytest 的问题,欢迎一起探讨!
评论