参数化是一种测试技术,它允许在测试过程中使用不同的输入参数运行相同的测试逻辑。
参数化的作用主要体现在以下几个方面:
参数化是测试框架中的一个重要特性,常用于单元测试、集成测试和功能测试等各个级别的测试。它可以提高测试的效率、可读性和维护性,帮助开发人员在不同的输入参数组合下更全面地验证系统的行为。
最常用的是通过装饰器
@pytest.mark.parametrize()
方式进行参数化。
import pytest
def add(a, b):
return a + b
@pytest.mark.parametrize("a,b", [[1, 2], [3, 4], [5, 6]])
# 第一个参数是以字符串的形式标识用例函数的参数。 -- "a,b"
# 第二个参数以列表或元组的形式传递测试数据。 -- [[1, 2], [3, 4], [5, 6]]
def test_case(a, b):
# 断言 a,b 之和是否小于等于 10。
assert add(a, b) <= 10
# test-demo.py::test_case[1-2] PASSED [ 33%]
# test-demo.py::test_case[3-4] PASSED [ 66%]
# test-demo.py::test_case[5-6] FAILED [100%]
这里以读取 yaml 文件为例,同样也可以将测试数据通过 json 文件进行管理。
F:\PROJECTS\TEST-DEMO
│
├─cases
│ test_login.py
│ __init__.py
│
├─data
│ login.yaml
│ __init__.py
│
├─utils
│ file_utils.py
│ __init__.py
└─
- case_name: "账号密码正确"
user_name: "root"
password: 666666
- case_name: "用户名为空"
user_name:
password: 666666
- case_name: "密码为空"
user_name: "root"
password:
import yaml
def read_yaml(yaml_file_path):
"""
读取 YAML 文件并返回解析后的数据。
:param yaml_file_path: YAML 文件的路径
:return: 解析后的 YAML 数据
:raises: IOError, yaml.YAMLError
"""
try:
with open(yaml_file_path, "r", encoding="utf-8") as f:
value = yaml.safe_load(stream=f)
except IOError:
# 处理文件读取错误。
raise
except yaml.YAMLError:
# 处理 YAML 解析错误。
raise
return value
if __name__ == '__main__':
print(read_yaml(r'../data/login.yaml'))
import pytest
from utils.file_utils import read_yaml
@pytest.mark.parametrize("case", read_yaml(r"../data/login.yaml"))
def test_login(case):
user_name = case["user_name"]
password = case["password"]
assert user_name == "root" and password == 666666
# PASSED [ 33%]
# {'case_name': '用户名为空', 'password': 666666, 'user_name': None} FAILED [ 66%]
# {'case_name': '密码为空', 'password': None, 'user_name': 'root'} FAILED [100%]
测试类的参数化,其实际上也是对类中的测试方法进行参数化。类中的测试方法的参数必须与
@pytest.mark.parametrize()
中的标识的参数个数一致。
import pytest
def add(a, b):
return a + b
@pytest.mark.parametrize("a,b,c", [[1, 2, 3], [1, 3, 4], [2, 5, 7]])
class TestParams:
def test_par1(self, a, b, c):
# 断言 a + b 是否等于 c。
assert add(a, b) == c
def test_par2(self, a, b, c):
assert add(a, b) == c
# def test_par3(self, a, b):
# 参数个数不一致,报错。
# assert add(a, b) > 0
# In test_par3: function uses no argument 'c'
# test_demo.py::TestParams::test_par1[1-2-3]
# test_demo.py::TestParams::test_par1[1-3-4]
# test_demo.py::TestParams::test_par1[2-5-7]
# test_demo.py::TestParams::test_par2[1-2-3]
# test_demo.py::TestParams::test_par2[1-3-4]
# test_demo.py::TestParams::test_par2[2-5-7]
#
# ============================== 6 passed in 0.01s ==============================
import pytest
@pytest.fixture(scope="module")
def login(request):
return request.param
data = [
{"name": "jan", "age": 28},
{"name": "rose", "age": 19}
]
@pytest.mark.parametrize("login", data, indirect=True)
def test_login(login):
name = str(login["name"])
age = int(login["age"])
assert name.startswith("j") and age > 25
# ========================= 1 failed, 1 passed in 0.11s =========================
indirect=True
: 这是一个可选参数,它告诉 pytest 在运行测试函数时将参数作为 fixture
解析并注入到测试函数中。通过设置 indirect=True
,你可以通过定义一个与参数名称相同的 fixture
函数来提供参数的值。通过自定义的 ids,可以提高过程参数的可读性。
import pytest
def add(a, b):
return a + b
def get_data():
"""
测试数据列表。
:return:
"""
return [[1, 2, 3], [1, 3, 4], [2, 5, 7]]
# 自定义输出信息。
ids = ["{} + {} = {}".format(a, b, c) for a, b, c in get_data()]
@pytest.mark.parametrize("a,b,c", get_data(), ids=ids)
class TestParams:
def test_par1(self, a, b, c):
# 断言 a + b 是否等于 c。
assert add(a, b) == c
# test-demo.py::TestParams::test_par1[1 + 2 = 3]
# test-demo.py::TestParams::test_par1[1 + 3 = 4]
# test-demo.py::TestParams::test_par1[2 + 5 = 7]
#
# ============================== 3 passed in 0.01s ==============================
还有一些其他参数化的方式。
pytest_generate_tests
是 conftest.py
文件中的一个钩子函数,用于自定义参数化测试用例的生成过程。通过编写自定义的 pytest_generate_tests
函数,可以定义自己的参数化方案或扩展 pytest
框架默认的参数化行为。“-------怕什么真理无穷,进一寸有一寸的欢喜。”
微信公众号搜索:饺子泡牛奶。