parameterized
库介绍
parameterized
是一个Python库,提供了简洁而强大的接口来实现参数化测试。支持多种Python测试框架,包括nose
、pytest
和unittest
。这意味着无论你使用哪种测试框架,都可以轻松集成parameterized
来享受参数化测试带来的便利。
安装parameterized
使用pip
来安装:
pip install parameterized
使用示例
下面是一些使用parameterized
进行参数化测试的示例。
使用@parameterized
装饰器
假设你有一个简单的函数add(a, b)
,你希望测试它在不同输入下的行为。你可以使用@parameterized
装饰器来定义一个参数化测试:
from parameterized import parameterized
import unittest
class TestMathFunctions(unittest.TestCase):
@parameterized.expand([(1, 2, 3), (4, 5, 9), (-1, -1, -2)])
def test_add(self, a, b, expected):
self.assertEqual(a + b, expected)
if __name__ == '__main__':
unittest.main()
示例中,@parameterized.expand
装饰器接受一个包含输入参数和预期结果的列表。对于列表中的每组参数,测试框架都会生成一个名为test_add_xxx
的测试用例,其中xxx
是参数的哈希值或索引(取决于parameterized
的版本)。然后,测试框架会运行这些测试用例,并验证a + b
的结果是否与expected
相等。
使用param()
函数
有时,你可能希望为参数提供更具描述性的名称,或者在参数之间建立更复杂的依赖关系。这时,可以使用param()
函数来定义参数:
from parameterized import parameterized, param
import unittest
class TestMathFunctions(unittest.TestCase):
@parameterized.expand([
param(a=1, b=2, expected=3, id='test_add_positive_numbers'),
param(a=4, b=-2, expected=2, id='test_add_positive_and_negative'),
param(a=-1, b=-1, expected=-2, id='test_add_negative_numbers')
])
def test_add_with_params(self, a, b, expected):
self.assertEqual(a + b, expected)
if __name__ == '__main__':
unittest.main()
示例中,param()
函数允许你为每个参数指定一个名称,并使用id
关键字为生成的测试用例指定一个唯一的标识符。这使得测试报告更加清晰,并有助于你快速定位失败的测试用例。
在pytest
中使用parameterized
虽然parameterized
最初是为nose
和unittest
设计的,但它也完全兼容pytest
。只需将测试类和方法定义为通常的pytest
风格,并使用@parameterized.expand
装饰器来添加参数:
import pytest
from parameterized import parameterized, param
@pytest.mark.parametrize('test_input,expected', [
(param(1, 2, 3, id='test_add_1_2_3'),), # Note the extra comma for a single-element tuple
(param(4, 5, 9, id='test_add_4_5_9'),),
(param(-1, -1, -2, id='test_add_negative'),),
])
def test_add_with_pytest(test_input, expected):
a, b, result = test_input # Unpack the param object
assert a + b == result
# Alternatively, you can use the @parameterized.expand decorator directly:
class TestMathWithPytest:
@parameterized.expand([
param(a=1, b=2, expected=3, id='test_add_class_1_2_3'),
param(a=4, b=5, expected=9, id='test_add_class_4_5_9'),
])
def test_add_in_class(self, a, b, expected):
assert a + b == expected
请注意,在pytest
中使用@parameterized.expand
时,需要将参数作为一个元组传递给param()
函数(即使只有一个参数),并在测试函数中解包该元组。这是因为pytest
的参数化机制期望每个测试用例都接收一个固定的参数列表。然而,从parameterized
0.7.0版本开始,可以直接在pytest
测试函数上使用@parameterized.expand
装饰器,而无需将参数包装在元组中。这在类方法中尤其有用,因为它允许更直观地传递和访问参数。
另外,虽然上面的示例中使用了pytest.mark.parametrize
来演示如何在pytest
中手动参数化测试,但通常使用@parameterized.expand
装饰器来获得更好的集成和更简洁的代码。
parameterized
的高级用法
除了基本的参数化测试之外,parameterized
还提供了许多高级功能,以满足更复杂的测试需求。
动态生成测试用例
有时,可能希望根据某些条件动态生成测试用例。这时,可以将参数列表定义为一个返回迭代器的函数,并将其传递给@parameterized.expand
装饰器:
import itertools
from parameterized import parameterized
import unittest
def generate_test_cases():
for a in range(1, 4):
for b in range(1, 4):
yield a, b, a * b
class TestMultiplication(unittest.TestCase):
@parameterized.expand(generate_test_cases())
def test_multiply(self, a, b, expected):
self.assertEqual(a * b, expected)
if __name__ == '__main__':
unittest.main()
示例中,generate_test_cases()
函数使用两个嵌套的for
循环来生成所有可能的(a, b)
组合,并计算它们的乘积作为预期结果。然后,@parameterized.expand
装饰器接受这个函数作为参数,并为每组生成的参数运行测试。
自定义测试用例名称
默认情况下,parameterized
会为生成的测试用例分配一个基于参数哈希值或索引的名称。但是,可能希望使用更具描述性的名称来更好地反映测试的目的。这时,可以使用name_func
关键字参数来自定义测试用例名称:
from parameterized import parameterized
import unittest
def custom_name_func(testcase_func, param_num, param):
return f"{
testcase_func.__name__}_{
param.args[0]}_{
param.args[1]}"
class TestCustomization(unittest.TestCase):
@parameterized.expand([(1, 2), (3, 4)], name_func=custom_name_func)
def test_with_custom_name(self, a, b):
self.assertTrue(a < b) # Just an example assertion
if __name__ == '__main__':
unittest.main()
示例中,custom_name_func
函数接受三个参数:testcase_func
(要测试的函数)、param_num
(参数的索引)和param
(一个包含参数值的对象)。然后,它返回一个字符串作为测试用例的名称。在这个例子中,测试用例的名称将是test_with_custom_name_1_2
和test_with_custom_name_3_4
。
与其他测试框架的集成
除了unittest
和pytest
之外,parameterized
还支持与nose
集成。只需按照通常的方式编写测试,并使用@parameterized.expand
装饰器来添加参数即可。parameterized
会自动处理与nose
的集成细节。