基于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_one
和test_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_fixture
将pytest.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文档68-pytest-lazy-fixture 插件解决 pytest.mark.parametrize 中使用 fixture 问题