基于fixture的自动pytest.mark装饰
Automatic pytest.mark decoration based on fixture
假设我在我的 conftest.py 文件中建立了一个 pytest fixture,它看起来像:
def live_fixture():
# network access here...
pass
我在很多测试功能中使用了相同的夹具,说 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_spam.py 内部 test_one
和 test_three
使用那个 live_fixture
因此应该有效地装饰 @pytest.mark.live
?
好吧,我又在这上面胡闹了一点,得到了一些非常粗糙的工作。我定义了一个这样的函数:
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__)
,它似乎使用 [=] 对该模块中的所有测试函数应用 pytest.mark.live
修饰13=]。但是这种方法至少存在两个问题:
- 我相信这只适用于定义在测试模块顶层的测试函数,我怀疑它是否适用于 test class 中的测试函数。
- 任何具有例如
@mock.patch
或其他常见的测试装饰将 not 有适当的参数内省可用于 inspect.getargspec (这是 decorator 模块存在的原因),所以他们不会像预期的那样装饰。
诚然这有点难看,也许有人有更简洁的解决方案?
我找到了另一个看似合理的解决方案,通过尝试挖掘 pytest.mark 的工作原理。
我发现 class Node 有一个方法 "add_marker",它应该是实现功能 pytest.mark 的确切方法。并且 class 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')
我希望至少我的回答能激发人们想出更体面的方法。
假设我在我的 conftest.py 文件中建立了一个 pytest fixture,它看起来像:
def live_fixture():
# network access here...
pass
我在很多测试功能中使用了相同的夹具,说 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_spam.py 内部 test_one
和 test_three
使用那个 live_fixture
因此应该有效地装饰 @pytest.mark.live
?
好吧,我又在这上面胡闹了一点,得到了一些非常粗糙的工作。我定义了一个这样的函数:
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__)
,它似乎使用 [=] 对该模块中的所有测试函数应用 pytest.mark.live
修饰13=]。但是这种方法至少存在两个问题:
- 我相信这只适用于定义在测试模块顶层的测试函数,我怀疑它是否适用于 test class 中的测试函数。
- 任何具有例如
@mock.patch
或其他常见的测试装饰将 not 有适当的参数内省可用于 inspect.getargspec (这是 decorator 模块存在的原因),所以他们不会像预期的那样装饰。
诚然这有点难看,也许有人有更简洁的解决方案?
我找到了另一个看似合理的解决方案,通过尝试挖掘 pytest.mark 的工作原理。
我发现 class Node 有一个方法 "add_marker",它应该是实现功能 pytest.mark 的确切方法。并且 class 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')
我希望至少我的回答能激发人们想出更体面的方法。