次の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
内のすべての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フィクスチャでは、関数はその戻り値に「なります」が、テストがパラメータ化された時点ではまだ起こっていないようです。希望する方法でテストを設定するにはどうすればよいですか?
https://bitbucket.org/pytest-dev/pytest/issues/349/using-fixtures-in-pytestmarkparametrize から、現在のところpytest.mark.parametrize
。
これは、実際には 間接パラメーター化 によって可能です。
この例では、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__])
この同様の質問 も参照してください。
間接的なパラメーター化、または継承を含む以下の私のハックな解決策の代わりに、params
引数を@pytest.fixture()
に使用することもできます-これはおそらく最も簡単な解決策だと思いますか?
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/fixture.html#parametrizing-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
フラグを渡す必要がないことです。
人々はフィクスチャとパラメータは同じものだと考える傾向があるため、これは混乱を招くトピックです。これらは、pytest実行の同じフェーズでは収集されず、収集されません。設計上、パラメーターは、テストがcollected(テストノードのリストが作成中)のときに収集されることを意図していますが、フィクスチャは、テストノードはrunです(テストノードのリストは固定されており、変更できません)。そのため、フィクスチャの内容を使用してテストノードの数を変更することはできません。これがパラメーターの役割です。 my 詳細な回答はこちら も参照してください。
これがあなたの質問に「現状のまま」解決策がない理由です: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's answer は、私見があなたの質問に直接関連していないので、私見が混乱を追加する別のトピックに言及しています。言及されており、解決策として受け入れられているので、詳しく説明しましょう。実際、pytestでは、@pytest.mark.parametrize
でフィクスチャを使用できません。しかし、それができても何も変わりません。
この機能はpytest-cases
でベータ版として利用できるようになったので(私は著者です)、自分で試してみてください。その機能を使用してサンプルを機能させる唯一の方法は、フィクスチャーからリストを削除するという同じです。したがって、次のようになります。
import pytest
from pytest_cases import pytest_parametrize_plus, fixture_ref
class TimeLine(object):
instances = [0, 1, 2]
@pytest.fixture(params=TimeLine().instances)
def timeline(request):
return request.param
@pytest_parametrize_plus("t", [fixture_ref(timeline)])
def test_timeline(t):
assert t % 2 == 0
これは前の例と同じですが、余分な、おそらく役に立たないレイヤーがあります。注: この説明 も参照してください。
Paramsetは関数を参照しています。おそらく、フィクスチャ関数/ timeline /ではなく、Timeline.instancesを参照することを意味します。
@pytest.mark.parametrize("instance", Timeline.instances)
def test_func(instance):
pass
おそらく理由がありますが、1つのパラメーターセットにマーク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