[英]How to parametrize a Pytest fixture
考慮以下 Pytest:
import pytest
class TimeLine(object):
instances = [0, 1, 2]
@pytest.fixture
def timeline():
return TimeLine()
def test_timeline(timeline):
for instance in timeline.instances:
assert instance % 2 == 0
if __name__ == "__main__":
pytest.main([__file__])
測試test_timeline
使用 Pytest 固定裝置timeline
,它本身具有屬性instances
。 該屬性在測試中被迭代,因此只有當該斷言對timeline.instances
.instances 中的每個instance
成立時,測試才會通過。
然而,我真正想做的是生成 3 個測試,其中 2 個應該通過,其中 1 個將失敗。 我試過了
@pytest.mark.parametrize("instance", timeline.instances)
def test_timeline(timeline):
assert instance % 2 == 0
但這導致
AttributeError: 'function' object has no attribute 'instances'
據我了解,在 Pytest 固定裝置中,函數“變成”了它的返回值,但是在測試參數化時這似乎還沒有發生。 如何以所需的方式設置測試?
這實際上可以通過間接參數化來實現。
此示例使用 pytest 3.1.2 執行您想要的操作:
import pytest
class TimeLine:
def __init__(self, instances):
self.instances = instances
@pytest.fixture
def timeline(request):
return TimeLine(request.param)
@pytest.mark.parametrize(
'timeline',
([1, 2, 3], [2, 4, 6], [6, 8, 10]),
indirect=True
)
def test_timeline(timeline):
for instance in timeline.instances:
assert instance % 2 == 0
if __name__ == "__main__":
pytest.main([__file__])
另請參閱此類似問題。
而不是間接參數化,或者我下面涉及繼承的hacky解決方案,你也可以只使用@pytest.fixture()
的params
參數——我認為這可能是最簡單的解決方案?
import pytest
class TimeLine:
def __init__(self, instances=[0, 0, 0]):
self.instances = instances
@pytest.fixture(params=[
[1, 2, 3], [2, 4, 5], [6, 8, 10]
])
def timeline(request):
return TimeLine(request.param)
def test_timeline(timeline):
for instance in timeline.instances:
assert instance % 2 == 0
https://docs.pytest.org/en/latest/how-to/fixtures.html#parametrizing-fixtures
這是一個令人困惑的話題,因為人們往往認為燈具和參數是一回事。 它們不是,甚至不會在 pytest 運行的同一階段收集。 按照設計,參數是在收集測試時收集的(測試節點列表正在構建中),而夾具則是在測試節點運行時執行(測試節點列表是固定的,不能修改)。 所以夾具內容不能用來改變測試節點的數量——這就是參數的作用。 另請參閱我的詳細答案here 。
這就是為什么您的問題沒有“原樣”解決方案的原因:您應該將Timeline
實例放在參數中,而不是固定裝置中。 像這樣:
import pytest
class TimeLine(object):
instances = [0, 1, 2]
@pytest.fixture(params=TimeLine().instances)
def timeline(request):
return request.param
def test_timeline(timeline):
assert timeline % 2 == 0
Kurt Peek 的回答提到了另一個主題,恕我直言在這里增加了混淆,因為它與您的問題沒有直接關系。 由於它被提及,甚至被接受為解決方案,讓我詳細說明:確實在 pytest 中,您不能在@pytest.mark.parametrize
使用夾具。 但即使你能做到,也不會改變任何事情。
由於此功能現在在pytest-cases
作為測試版提供(我是作者),您可以自己嘗試一下。 即使使用該功能,使您的示例工作的唯一方法仍然相同:從夾具中刪除列表。 所以你最終得到:
import pytest
from pytest_cases import parametrize, fixture_ref
class TimeLine(object):
instances = [0, 1, 2]
@pytest.fixture(params=TimeLine().instances)
def timeline(request):
return request.param
@parametrize("t", [fixture_ref(timeline)])
def test_timeline(t):
assert t % 2 == 0
這與前面的示例相同,但有一個額外的,可能沒用的層。 注意:另請參閱此討論。
從Using fixtures in pytest.mark.parametrize pytest issue 來看,目前似乎無法在pytest.mark.parametrize
使用 fixtures。
間接參數化的使用是有效的,但我發現需要使用request.param
作為一個神奇的、未命名的變量有點尷尬。
這是我使用過的一種模式。 可以說,它以不同的方式尷尬,但也許你也會更喜歡它!
import pytest
class TimeLine:
def __init__(self, instances):
self.instances = instances
@pytest.fixture
def instances():
return [0, 0, 0]
@pytest.fixture
def timeline(instances):
return TimeLine(instances)
@pytest.mark.parametrize('instances', [
[1, 2, 3], [2, 4, 5], [6, 8, 10]
])
def test_timeline(timeline):
for instance in timeline.instances:
assert instance % 2 == 0
timeline
夾具依賴於另一個名為instances
夾具,其默認值為[0,0,0]
,但在實際測試中,我們使用parametrize
化為instances
注入一系列不同的值。
在我看來,優點是一切都有一個好名字,而且您不需要傳遞該indirect=True
標志。
您的參數化 paramset 正在引用該函數。 也許您的意思是引用 Timeline.instances,而不是夾具函數 /timeline/:
@pytest.mark.parametrize("instance", Timeline.instances)
def test_func(instance):
pass
我認為您希望將標記 xfail 添加到一組參數中,也許是有原因的?
def make_my_tests():
return [0, 2, mark.xfail(1, reason='not even')]
@mark.parametrize('timeline', paramset=make_my_tests(), indirect=True)
def test_func(timeline):
assert timeline % 2 == 0
我解決了創建不帶fixture
裝飾器的函數和應用裝飾器的先前函數的問題:
def one_raw():
return 1
one = pytest.fixture(one_raw)
def test_add(one):
assert one + 2 == 3
@pytest.mark.parametrize("subtrahend", (one_raw(), int(True)))
def test_sub(subtrahend):
assert 4 - subtrahend == 3
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.