【问题标题】:pytest: passing keyword arg to fixture using pytest.mark.parametrize with indirect parameterizationpytest:使用带有间接参数化的 pytest.mark.parametrize 将关键字 arg 传递给夹具
【发布时间】:2021-01-02 14:19:01
【问题描述】:

我有一个pytest.fixture,它有一个位置参数和一个关键字参数。

根据Pass a parameter to a fixture function,可以使用pytest.mark.parametrize 将参数传递给夹具,并将indirect arg 设置为夹具的名称。

请看下面的示例代码。

import pytest

class Foo:
    def __init__(self, a: str, b: str):
        self.a = a
        self.b = b

@pytest.fixture
def a() -> str:
    return "alphabet"

@pytest.fixture
def foo_obj(a: str, b: str = "bar") -> Foo:
    return Foo(a, b)

@pytest.mark.parametrize("foo_obj", [("applesauce", "baz")], indirect=["foo_obj"])
def test_thing(foo_obj) -> None:
    assert foo_obj.a == "applesauce"
    assert foo_obj.b == "baz"

此测试当前失败:"applesauce""baz" 没有被传递到夹具 foo_obj

我的问题:

  1. 将 args 传递给夹具 foo_obj 时我做错了什么?
  2. 是否可以只在pytest.mark.parametrize装饰器调用中输入kwarg b

版本

Python==3.8.5
pytest==6.0.1

【问题讨论】:

    标签: python pytest fixtures


    【解决方案1】:

    answer from @MrBeanBremen 让我感受到了 this answer 的味道,它谈到了隐式间接参数化。

    @pytest.fixture
    def foo_obj(a: str, b: str) -> Foo:  # Note: b was changed to positional arg
        print("hi")
        return Foo(a, b)
    
    @pytest.mark.parametrize("a, b", [("applesauce", "baz")])
    def test_thing(foo_obj) -> None:
        assert foo_obj.a == "applesauce"
        assert foo_obj.b == "baz"
    

    在上述情况下测试通过。

    似乎这种隐式间接参数化可以用于位置参数,但不能用于关键字参数。例如,如果 b 保留在夹具 foo_obj 中作为关键字参数,则 pytest 将失败并显示以下消息:In test_thing: function uses no argument 'b'

    我想知道,是否可以以这种方式对关键字参数进行参数化?


    第二次尝试使用关键字 arg

    我决定再次尝试answer from @MrBeanBremen 以及这个答案:Provide default argument value for py.test fixture function

    @pytest.fixture
    def a() -> str:
        return "alphabet"
    
    @pytest.fixture
    def foo_obj(request, a) -> Foo:
        return Foo(request.param.get("a", a), request.param.get("b", "bar"))
    
    @pytest.mark.parametrize("foo_obj", [dict(a="applesauce", b="baz")], indirect=True)
    def test_thing(foo_obj) -> None:
        # Override both defaults, passes
        assert foo_obj.a == "applesauce"
        assert foo_obj.b == "baz"
    
    @pytest.mark.parametrize("foo_obj", [dict(b="baz")], indirect=True)
    def test_thing_only_b(foo_obj) -> None:
        # Use the default for a
        assert foo_obj.a == "alphabet"
        assert foo_obj.b == "baz"
    

    这行得通!我觉得它与foo_obj 夹具接受两个参数有点令人费解,而它有时使用一个或另一个。

    【讨论】:

    • 啊,太好了!我想我以前见过这个,但完全忘记了 - 我在文档中找不到它。
    • 好的,现在检查一下@MrBeanBremen。关于如何清理第二部分的任何想法?
    • 这对我来说看起来非常好 - 使用 dict.get 来设置一个表现得像关键字 arg 的默认值的方法可能是你能做的最好的事情。至于 2 个参数(如果您的意思是 requesta) - 它们用于不同的目的。第一个需要获取参数化值,第二个允许在单独的夹具中自定义一个默认值,因此只要您自定义一个默认值(或两者),您需要额外的夹具参数(或两个。 ..)
    【解决方案2】:

    我认为您在这里混合了两件事:通过request.params 将参数传递给夹具,并将夹具基于另一个夹具(或多个夹具)。

    要在您的夹具中使用mark.parametrize 中使用的参数,您必须从request.params 获取它们,如您的链接问题所示:

    @pytest.fixture
    def foo_obj(request) -> Foo:
        return Foo(request.param[0], request.param[1])
    

    您正在做的事情是基于夹具a 上的foo_obj,这意味着a 是该夹具的结果(例如,它始终是"a")和一个常量参数b .两者都与parametrize 设置的值无关 - 这些设置在request.params 中,如上所示。

    【讨论】:

    • 啊是的,这就是问题所在,我错过了.params。我想知道,request arg 名称是pytest 中的约定吗?
    • 不,它是一个关键字参数(如灯具),你必须使用它。
    • 啊,我看到它记录在here。是的,我是pytest的新手
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-05
    • 1970-01-01
    • 2022-10-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多