基于fixture的自动pytest.mark装饰

Posted

技术标签:

【中文标题】基于fixture的自动pytest.mark装饰【英文标题】:Automatic pytest.mark decoration based on fixture 【发布时间】:2016-10-23 06:38:26 【问题描述】:

假设我在我的 conftest.py 文件中建立了一个 pytest 固定装置,如下所示:

def live_fixture():
    # network access here...
    pass

我在很多测试函数中都使用了同一个fixture,比如说test_spam.py有一些测试函数:

@pytest.mark.live
def test_one(live_fixture):
    assert 1


def test_one():
    assert 2 

@pytest.mark.live
def test_three(live_fixture):
    assert 3

我在第一个和第三个测试函数上使用@pytest.mark.live 装饰,因为这两个测试都依赖于夹具live_fixture,它通过网络输出并执行操作。理由:我喜欢让我的测试的可靠子集离线通过,例如

py.test -m "not live" test_spam.py --blockage

将可靠地通过(使用漂亮的pytest-blockage 模块来强制执行无网络访问限制)。

但是在每个使用live_fixture 的测试函数上写出@pytest.mark.live 装饰是乏味且容易出错的。有没有办法让该夹具声明任何使用它的测试函数都应该自动应用@pytest.mark.live 装饰,或者以某种方式检测test_onetest_three 使用live_fixture 的内部文件test_spam.py因此应该有效地装饰@pytest.mark.live

【问题讨论】:

【参考方案1】:

好吧,我在这个问题上又胡闹了一点,得到了一些非常粗糙的工作。我定义了一个这样的函数:

import sys, inspect, pytest

def mark_live_fixtures(module_name):
    testfunctions = [obj for name,obj
                     in inspect.getmembers(sys.modules[module_name])
                     if (inspect.isfunction(obj) and
                         name.startswith('test') and name != 'testall')]

    for func in testfunctions:
        if 'live_fixture' in inspect.getargspec(func).args:
            func = pytest.mark.live(func)

然后我可以在测试文件的底部调用mark_live_fixtures(__name__),并且似乎使用live_fixturepytest.mark.live 装饰应用于该模块中的所有测试函数。但是这种方法至少存在两个问题:

    我相信这只适用于在测试模块顶层定义的测试函数,我怀疑它是否适用于test class 中的测试函数。 任何测试功能,例如@mock.patch 或其他常见的测试修饰将具有可用于 inspect.getargspec 的适当参数自省(这是 decorator 模块存在的原因),因此它们不会按预期进行修饰。李>

这确实有点难看,也许有人有更清洁的解决方案?

【讨论】:

【参考方案2】:

我通过尝试了解 pytest.mark 的工作原理找到了另一个可行的解决方案。

我发现类 Node 有一个方法“add_marker”,它应该是实现功能 pytest.mark 的确切方法。并且类 Item 是从 Node 扩展而来的。

所以我的解决方案是:

    尝试查找该夹具是否被测试使用。 让测试的Item对象调用add_marker

示例:在 conftest.py 中添加如下方法

def pytest_itemcollected(item):
    """ we just collected a test item. """
    if 'live_fixture' in item.fixturenames:
        item.add_marker('live')

我希望至少我的回答能激发人们思考更体面的方式。

【讨论】:

这是一个非常巧妙的解决方案,谢谢!我还希望使用live_fixture 自动将pytest.mark.skipif 装饰应用到任何测试函数上,你知道这是否可能吗? @JoshKupershmidt 是的,add_marker 也将MarkDecorator 对象作为输入,pytest.mark.skipif 返回一个MarkDecorator 对象。所以你需要做的就是创建skipif标记并添加它。例如:marker = pytest.mark.skipif(condition, reason=''),然后是item.add_marker(marker)

以上是关于基于fixture的自动pytest.mark装饰的主要内容,如果未能解决你的问题,请参考以下文章

pytest之fixture

Pytest

pytest之fixture

pytest运行方式及装饰器的使用

pytest-conftest.py作用范围

pytest文档68-pytest-lazy-fixture 插件解决 pytest.mark.parametrize 中使用 fixture 问题