当前位置: 首页 > 知识库问答 >
问题:

在函数装饰器中使用pytestfixture

孟璞
2023-03-14

我想为我的测试函数构建一个具有多种用途的装饰器。其中之一是帮助向生成的junitxml添加属性。

我知道有一个名为record\u属性的fixture内置pytest,它正好做到了这一点。我如何在我的装饰器内使用这个固定装置?

def my_decorator(arg1):
    def test_decorator(func):
        def func_wrapper():
            # hopefully somehow use record_property with arg1 here
            # do some other logic here
            return func()
        return func_wrapper
    return test_decorator

@my_decorator('some_argument')
def test_this():
    pass # do actual assertions etc.

我知道我可以将夹具直接传递到每个测试函数中,并在测试中使用它,但我有很多测试,这样做似乎非常多余。

另外,我知道我可以使用conftest.py创建一个自定义标记并在decorator中调用它,但是我有很多conftest.py文件,我不能单独管理所有这些文件,因此无法强制执行。

最后,尝试将该装置直接导入到我的decorator模块中,然后使用它会导致一个错误-因此,这也是不可能的。

谢谢你的帮助

共有1个答案

丌官霖
2023-03-14

有点晚了,但是我在我们的代码库中遇到了同样的问题。我可以找到一个解决方案,但它是相当hacky,所以我不会保证它的工作与旧版本或将在未来盛行。

因此,我问是否有更好的解决办法。您可以在这里查看:如何在decorator中使用pytest fixture,而不将其作为修饰函数的参数

其基本思想是注册经过修饰的测试函数,然后诱使pytest认为它们需要参数列表中的fixture:

class RegisterTestData:
    # global testdata registry
    testdata_identifier_map = {} # Dict[str, List[str]]

    def __init__(self, testdata_identifier, direct_import = True):
        self.testdata_identifier = testdata_identifier
        self.direct_import = direct_import
        self._always_pass_my_import_fixture = False

    def __call__(self, func):
        if func.__name__ in RegisterTestData.testdata_identifier_map:
            RegisterTestData.testdata_identifier_map[func.__name__].append(self.testdata_identifier)
        else:
            RegisterTestData.testdata_identifier_map[func.__name__] = [self.testdata_identifier]

        # We need to know if we decorate the original function, or if it was already
        # decorated with another RegisterTestData decorator. This is necessary to 
        # determine if the direct_import fixture needs to be passed down or not
        if getattr(func, "_decorated_with_register_testdata", False):
            self._always_pass_my_import_fixture = True
        setattr(func, "_decorated_with_register_testdata", True)

        @functools.wraps(func)
        @pytest.mark.usefixtures("my_import_fixture") # register the fixture to the test in case it doesn't have it as argument
        def wrapper(*args: Any, my_import_fixture, **kwargs: Any):
            # Because of the signature of the wrapper, my_import_fixture is not part
            # of the kwargs which is passed to the decorated function. In case the
            # decorated function has my_import_fixture in the signature we need to pack
            # it back into the **kwargs. This is always and especially true for the
            # wrapper itself even if the decorated function does not have
            # my_import_fixture in its signature
            if self._always_pass_my_import_fixture or any(
                "hana_import" in p.name for p in signature(func).parameters.values()
            ):
                kwargs["hana_import"] = hana_import
            if self.direct_import:
                my_import_fixture.import_all()
            return func(*args, **kwargs)
        return wrapper

def pytest_collection_modifyitems(config: Config, items: List[Item]) -> None:
    for item in items:
        if item.name in RegisterTestData.testdata_identifier_map and "my_import_fixture" not in item._fixtureinfo.argnames:
            # Hack to trick pytest into thinking the my_import_fixture is part of the argument list of the original function
            # Only works because of @pytest.mark.usefixtures("my_import_fixture") in the decorator
            item._fixtureinfo.argnames = item._fixtureinfo.argnames + ("my_import_fixture",)
 类似资料:
  • 问题内容: 举个例子: 我遇到的问题是,甚至在我调用要装饰的函数之前就调用了。 开始输出: 在这一点上,我什至没有调用过一个装饰过的函数。 我刚刚开始使用装饰器,所以也许我缺少了一些东西。 问题答案: 我相信python装饰器只是语法糖。 和…一样 如您所见,即使没有调用 bar 也将调用 foo 。这就是为什么您看到装饰器函数的输出的原因。对于您将装饰器应用到的每个函数,您的输出应只包含一行。

  • 主要内容:带参数的函数装饰器,函数装饰器可以嵌套前面章节中,我们已经讲解了 Python 内置的 3 种函数装饰器,分别是 @staticmethod、@classmethod 和 @property,其中 staticmethod()、classmethod() 和 property() 都是 Python 的内置函数。 那么,函数装饰器的工作原理是怎样的呢?假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示: 实际上,上面

  • 问题内容: 我想这就是它们的称呼,但我会举一些例子以防万一。 装饰类: 装饰器功能: 使用一个或另一个只是口味的问题吗?有实际区别吗? 问题答案: 如果您可以编写函数来实现装饰器,则应首选它。但是并非所有装饰器都可以轻松地编写为一个函数-例如,当您要存储一些内部状态时。 我见过人们(包括我自己)经过荒唐的努力,只用函数编写装饰器。我仍然不知道为什么,一个班级的开销通常可以忽略不计。

  • 问题内容: 我很难理解修饰的递归函数是如何工作的。对于以下代码段: 输出为: 第一个打印f(n),因此很自然,每次递归调用f(n)时,它都会打印“原始”。 第二个打印def_f(n),因此当n传递给包装器时,它将递归调用f(n)。但是包装器本身不是递归的,因此仅打印一个“装饰”。 第三个让我感到困惑,这与使用装饰器@dec相同。为什么修饰的f(n)也调用包装器五次?在我看来,def_f = dec

  • 问题内容: 我想定义一些通用装饰器,以在调用某些函数之前检查参数。 就像是: 旁注: 类型检查只是在这里显示一个示例 我正在使用Python 2.7,但是Python 3.0也会很有趣 问题答案: 从装饰器的功能和方法: Python 2 Python 3 在Python 3中已更改为,并且已更改为。 用法: 可以是或

  • 举例说一下,比如有2个函数,我要对他们做相同的前置判断条件。