如何将pytest参数化与需要修改的输入一起使用?
How to use pytest parametrize with inputs that need to be modified?
我见过的大多数例子都显示了一个简单的例子,其中 inputs/outputs 可以用一行表示:
@pytest.mark.parametrize("test_input", [1, 2, 3, 4])
我应该如何处理需要修改一两行的参数?
例如:
test_input1 = User()
test_input1.name = 'John'
test_input2 = User()
test_input2.phone = '1234567890'
test_input2.address = '123 Main St'
test_input3 = User()
test_input3.initials.middle = 'A'
test_input4 = User()
test_input4.make_super_user()
假设我无法将它们作为构造函数参数传递,因此 User(name='John')
不是一个选项。
有几种方法可以做到这一点:
提供设置对象所需的信息,然后在测试函数中进行设置。这对于您的特定示例来说有点麻烦,其中设置不遵循常规模式,但它仍然有效:
@pytest.mark.parametrize(
'params', [
dict(
name='John',
),
dict(
phone='1234567890',
address='123 Main St',
),
dict(
initials=dict(middle='A'),
),
dict(
is_super_user=True,
),
]
)
def test_user(params):
user = User()
if 'name' in params:
user.name = params['name']
if 'phone' in params:
user.phone = params['phone']
if 'address' in params:
user.phone = params['address']
if 'initials' in params:
user.initials.middle = params['initials']['middle']
if params.get('is_super_user')
user.make_super_user()
assert ...
用工厂函数参数化测试函数。这个想法与 jonrsharpe 在评论中的建议略有不同,因为在这种情况下,您将为每个测试用例编写一个单独的工厂。也就是说,您可以通过使用类似于 jonrsharpe 的代码编写“工厂工厂”来减少示例 1 和 2 的样板代码:
def make_user_with_name():
user = User()
user.name = 'John'
return user
def make_user_with_phone_address():
user = User()
user.phone = '1234567890'
user.address = '123 Main St'
return user
def make_user_with_middle_initial():
user = User()
user.initial.middle = 'A'
return user
def make_super_user():
user = User()
user.make_super_user()
return user
@pytest.mark.parametrize(
'factory', [
make_user_with_name,
make_user_with_phone_address,
make_user_with_middle_initial,
make_super_user,
]
)
def test_user(factory):
user = factory()
assert ...
使用 exec()
从字符串创建对象。当您从单独的文件加载参数时,这是一个很好的方法(这是我强烈推荐的;参见 parametrize_from_file),尽管为了简单起见,下面的示例只包含 python 中的所有内容。请注意,每个片段都应定义一个名为 user
:
的全局变量
@pytest.mark.parametrize(
'snippet', [
"""\
user = User()
user.name = 'John'
""",
"""\
user = User()
user.phone = '1234567890'
user.address = '123 Main St'
""",
"""\
user = User()
user.initials.middle = 'A'
""",
"""\
user = User()
user.make_super_user()
""",
]
)
def test_user(snippet):
scope = {}
exec(snippet, scope)
user = scope['user']
assert ...
我见过的大多数例子都显示了一个简单的例子,其中 inputs/outputs 可以用一行表示:
@pytest.mark.parametrize("test_input", [1, 2, 3, 4])
我应该如何处理需要修改一两行的参数? 例如:
test_input1 = User()
test_input1.name = 'John'
test_input2 = User()
test_input2.phone = '1234567890'
test_input2.address = '123 Main St'
test_input3 = User()
test_input3.initials.middle = 'A'
test_input4 = User()
test_input4.make_super_user()
假设我无法将它们作为构造函数参数传递,因此 User(name='John')
不是一个选项。
有几种方法可以做到这一点:
提供设置对象所需的信息,然后在测试函数中进行设置。这对于您的特定示例来说有点麻烦,其中设置不遵循常规模式,但它仍然有效:
@pytest.mark.parametrize( 'params', [ dict( name='John', ), dict( phone='1234567890', address='123 Main St', ), dict( initials=dict(middle='A'), ), dict( is_super_user=True, ), ] ) def test_user(params): user = User() if 'name' in params: user.name = params['name'] if 'phone' in params: user.phone = params['phone'] if 'address' in params: user.phone = params['address'] if 'initials' in params: user.initials.middle = params['initials']['middle'] if params.get('is_super_user') user.make_super_user() assert ...
用工厂函数参数化测试函数。这个想法与 jonrsharpe 在评论中的建议略有不同,因为在这种情况下,您将为每个测试用例编写一个单独的工厂。也就是说,您可以通过使用类似于 jonrsharpe 的代码编写“工厂工厂”来减少示例 1 和 2 的样板代码:
def make_user_with_name(): user = User() user.name = 'John' return user def make_user_with_phone_address(): user = User() user.phone = '1234567890' user.address = '123 Main St' return user def make_user_with_middle_initial(): user = User() user.initial.middle = 'A' return user def make_super_user(): user = User() user.make_super_user() return user @pytest.mark.parametrize( 'factory', [ make_user_with_name, make_user_with_phone_address, make_user_with_middle_initial, make_super_user, ] ) def test_user(factory): user = factory() assert ...
使用
的全局变量exec()
从字符串创建对象。当您从单独的文件加载参数时,这是一个很好的方法(这是我强烈推荐的;参见 parametrize_from_file),尽管为了简单起见,下面的示例只包含 python 中的所有内容。请注意,每个片段都应定义一个名为user
:@pytest.mark.parametrize( 'snippet', [ """\ user = User() user.name = 'John' """, """\ user = User() user.phone = '1234567890' user.address = '123 Main St' """, """\ user = User() user.initials.middle = 'A' """, """\ user = User() user.make_super_user() """, ] ) def test_user(snippet): scope = {} exec(snippet, scope) user = scope['user'] assert ...