簡體   English   中英

如何參數化 Pytest 夾具

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM