【问题标题】:Define a pytest fixture providing multiple arguments to test function定义一个 pytest 夹具,为测试函数提供多个参数
【发布时间】:2018-06-07 21:23:14
【问题描述】:

使用 pytest,我可以像这样定义一个夹具:

@pytest.fixture
def foo():
    return "blah"

并在这样的测试中使用它:

def test_blah(foo):
    assert foo == "blah"

这一切都很好。但我想要做的是定义一个“扩展”以向测试函数提供 multiple 参数的单个夹具函数。像这样的:

@pytest.multifixture("foo,bar")
def foobar():
    return "blah", "whatever"

def test_stuff(foo, bar):
    assert foo == "blah" and bar == "whatever"

我想将两个对象 foobar 一起定义(而不是单独的装置),因为它们以某种方式相关。我有时可能还想定义一个依赖于另一个夹具的夹具,但让第二个夹具合并第一个夹具的结果并将其与自己的加法一起返回:

@pytest.fixture
def foo():
    return "blah"

@pytest.multifixture("foo,bar")
def foobar():
    f = foo()
    return f, some_info_related_to(f)

这个例子可能看起来很傻,但在某些情况下foo 类似于一个请求对象,而bar 对象需要链接到同一个请求对象。 (也就是说,我不能将foobar 定义为独立的fixture,因为我需要两者都来自一个请求。)

本质上,我想做的是将夹具函数的名称与测试函数参数的名称解耦,这样我就可以定义一个由特定 set 触发的夹具 em> 测试函数签名中的参数名称,而不仅仅是名称与夹具函数名称相同的单个参数。

当然,我总是可以只返回一个元组作为夹具的结果,然后自己在测试函数中解压它。但是鉴于 pytest 提供了各种神奇的技巧来自动将名称与参数匹配,看起来它也可以神奇地处理这个问题并不是不可想象的。 pytest 可以做到这一点吗?

【问题讨论】:

标签: python unit-testing pytest fixtures


【解决方案1】:

您现在可以使用pytest-cases

from pytest_cases import fixture

@fixture(unpack_into="foo,bar")
def foobar():
    return "blah", "whatever"

def test_stuff(foo, bar):
    assert foo == "blah" and bar == "whatever"

查看documentation了解更多详情(顺便说一下我是作者)

【讨论】:

  • 酷!具有讽刺意味的是,因为我的 github 票几周前刚刚作为 wontfix 关闭,但一个惊喜! :-)
  • 很高兴这对您有帮助 :) 您能否使用问题 url 更新您的评论,以便我可以找到它并在此处附加评论?谢谢!
【解决方案2】:

注意:如果您的灯具依赖于其他带参数的灯具,则此解决方案不起作用

不知道pytest包里有没有默认的解决方案,但是你可以自定义一个:

import pytest
from _pytest.mark import MarkInfo


def pytest_generate_tests(metafunc):
    test_func = metafunc.function
    if 'use_multifixture' in [name for name, ob in vars(test_func).items() if isinstance(ob, MarkInfo)]:
        result, func = test_func.use_multifixture.args
        params_names = result.split(',')
        params_values = list(func())
        metafunc.parametrize(params_names, [params_values])


def foobar():
    return "blah", "whatever"


@pytest.mark.use_multifixture("foo,bar", foobar)
def test_stuff(foo, bar):
    assert foo == "blah" and bar == "whatever"


def test_stuff2():
    assert 'blah' == "blah"

所以我们定义了 pytest_generate_tests 元函数。这个函数

  1. 检查 multifixture 标记是否在测试中
  2. 如果标记打开 - 它采用变量名称 "foo,bar" 和函数 foobar 将在生成时执行

    @pytest.mark.multifixture("foo,bar", foobar)

【讨论】:

    【解决方案3】:

    您可以使用两个 pytest 固定装置来执行此操作,如下所示:

    import pytest
    
    @pytest.fixture
    def foo():
        return [object()]
    
    # value derived from foo
    @pytest.fixture
    def bar(foo):
        return foo[0]
    
    # totally independent fixture
    @pytest.fixture
    def baz():
        return object()
    
    
    def test_fixtures(foo, bar, baz):
        assert foo[0] is bar
        assert foo[0] is not baz
        # both assertions will pass
    

    这里的 foo 和 bar 固定装置在它们的值之间具有特定的关系(引用同一个对象)。这与您希望从多夹具获得的结果相同。 (baz 夹具用于比较,并使用了一个不相关的 object() 实例。

    如果这两个值都来自某个共享上下文,您可以将共享上下文放在一个夹具中,然后独立导出最终结果。

    @pytest.fixture
    def shared():
        return [object()]
    
    @pytest.fixture
    def derived_1(shared):
        return shared[0]
    
    @pytest.fixture
    def derived_2(shared):
        return shared[-1]
    
    def test_derived(derived_1, derived_2):
        assert derived_1 is derived_2
    

    【讨论】:

      猜你喜欢
      • 2020-12-03
      • 1970-01-01
      • 1970-01-01
      • 2022-01-08
      • 2020-11-19
      • 2018-02-24
      • 2021-02-17
      • 1970-01-01
      • 2020-06-04
      相关资源
      最近更新 更多