首页 > 解决方案 > 如何对需要修改的输入使用 pytest 参数化?

问题描述

我见过的大多数示例都显示了一个简单的情况,其中输入/输出可以用一行表示:

@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')不是一个选项。

标签: pythonpytest

解决方案


有几种方法可以做到这一点:

  1. 提供设置对象所需的信息,然后在测试功能中进行设置。这对于您的特定示例来说有点麻烦,其中设置不遵循常规模式,但它仍然有效:

    @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 ...
    
  2. 使用工厂函数参数化测试函数。这个想法与 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 ...
    
  3. 用于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 ...
    

推荐阅读