188

Python クラス MyTester にラップされた DLL コードをテストするために py.test を使用しています。検証の目的で、テスト中にいくつかのテスト データをログに記録し、その後さらに処理を行う必要があります。多くの test_... ファイルがあるので、ほとんどのテストでテスター オブジェクトの作成 (MyTester のインスタンス) を再利用したいと考えています。

テスター オブジェクトは DLL の変数と関数への参照を取得したオブジェクトであるため、テスト ファイルごとに DLL の変数のリストをテスター オブジェクトに渡す必要があります (ログに記録される変数は test_.. 。 ファイル)。リストの内容は、指定されたデータを記録するために使用されます。

私の考えは、次のように何とかすることです:

import pytest

class MyTester():
    def __init__(self, arg = ["var0", "var1"]):
        self.arg = arg
        # self.use_arg_to_init_logging_part()

    def dothis(self):
        print "this"

    def dothat(self):
        print "that"

# located in conftest.py (because other test will reuse it)

@pytest.fixture()
def tester(request):
    """ create tester object """
    # how to use the list below for arg?
    _tester = MyTester()
    return _tester

# located in test_...py

# @pytest.mark.usefixtures("tester") 
class TestIt():

    # def __init__(self):
    #     self.args_for_tester = ["var1", "var2"]
    #     # how to pass this list to the tester fixture?

    def test_tc1(self, tester):
       tester.dothis()
       assert 0 # for demo purpose

    def test_tc2(self, tester):
       tester.dothat()
       assert 0 # for demo purpose

このように達成することは可能ですか、それとももっとエレガントな方法がありますか?

通常、ある種のセットアップ関数 (xUnit スタイル) を使用して、各テスト メソッドに対して実行できます。しかし、私はある種の再利用を得たいと思っています。これがフィクスチャで可能かどうかは誰にもわかりませんか?

私はこのようなことができることを知っています:(ドキュメントから)

@pytest.fixture(scope="module", params=["merlinux.eu", "mail.python.org"])

しかし、テストモジュールで直接パラメータ化する必要があります。 テスト モジュールからフィクスチャの params 属性にアクセスすることは可能ですか?

4

9 に答える 9

222

これは、実際には py.test で間接的なパラメーター化を介してネイティブにサポートされています。

あなたの場合、次のようになります。

@pytest.fixture
def tester(request):
    """Create tester object"""
    return MyTester(request.param)


class TestIt:
    @pytest.mark.parametrize('tester', [['var1', 'var2']], indirect=True)
    def test_tc1(self, tester):
       tester.dothis()
       assert 1
于 2015-11-23T19:34:42.043 に答える
167

更新:これはこの質問に対する受け入れられた回答であり、それでも時々賛成されるため、更新を追加する必要があります。私の最初の答え(以下)は、pytestの古いバージョンでこれを行う唯一の方法でしたが、の人が指摘したように、 pytestはフィクスチャの間接的なパラメーター化をサポートするようになりました。たとえば、次のようなことができます (@imiric 経由):

# test_parameterized_fixture.py
import pytest

class MyTester:
    def __init__(self, x):
        self.x = x

    def dothis(self):
        assert self.x

@pytest.fixture
def tester(request):
    """Create tester object"""
    return MyTester(request.param)


class TestIt:
    @pytest.mark.parametrize('tester', [True, False], indirect=['tester'])
    def test_tc1(self, tester):
       tester.dothis()
       assert 1
$ pytest -v test_parameterized_fixture.py
================================================================================= test session starts =================================================================================
platform cygwin -- Python 3.6.8, pytest-5.3.1, py-1.8.0, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: .
collected 2 items

test_parameterized_fixture.py::TestIt::test_tc1[True] PASSED                                                                                                                    [ 50%]
test_parameterized_fixture.py::TestIt::test_tc1[False] FAILED

ただし、この形式の間接パラメーター化は明示的ですが、@ Yukihiko Shinoda が指摘するように、暗黙的な間接パラメーター化の形式をサポートするようになりました (ただし、公式ドキュメントでこれに関する明確な参照は見つかりませんでした)。

# test_parameterized_fixture2.py
import pytest

class MyTester:
    def __init__(self, x):
        self.x = x

    def dothis(self):
        assert self.x

@pytest.fixture
def tester(tester_arg):
    """Create tester object"""
    return MyTester(tester_arg)


class TestIt:
    @pytest.mark.parametrize('tester_arg', [True, False])
    def test_tc1(self, tester):
       tester.dothis()
       assert 1
$ pytest -v test_parameterized_fixture2.py
================================================================================= test session starts =================================================================================
platform cygwin -- Python 3.6.8, pytest-5.3.1, py-1.8.0, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: .
collected 2 items

test_parameterized_fixture2.py::TestIt::test_tc1[True] PASSED                                                                                                                   [ 50%]
test_parameterized_fixture2.py::TestIt::test_tc1[False] FAILED

この形式のセマンティクスが正確にはわかりませんが、メソッドは という名前の引数を取らないが、使用しているフィクスチャはそうするため、パラメータ化された引数をフィクスチャを介して渡すことをpytest.mark.parametrize認識しているようです。test_tc1tester_argtestertester


私は同様の問題を抱えていました。私は というフィクスチャを持っていて、test_package後で特定のテストで実行するときにオプションの引数をそのフィクスチャに渡せるようにしたいと考えました。例えば:

@pytest.fixture()
def test_package(request, version='1.0'):
    ...
    request.addfinalizer(fin)
    ...
    return package

(これらの目的では、フィクスチャが何をするか、または返されるオブジェクトのタイプは重要ではありませんpackage)。

versionその場合、そのテストで使用するフィクスチャへの引数も指定できるように、テスト関数でこのフィクスチャを使用することが望ましいでしょう。素晴らしい機能になるかもしれませんが、これは現在不可能です。

それまでの間、フィクスチャが以前に実行したすべての作業を実行する関数を単純に返すようにするのは簡単でしたが、version引数を指定できます。

@pytest.fixture()
def test_package(request):
    def make_test_package(version='1.0'):
        ...
        request.addfinalizer(fin)
        ...
        return test_package

    return make_test_package

これで、次のようなテスト関数でこれを使用できます。

def test_install_package(test_package):
    package = test_package(version='1.1')
    ...
    assert ...

等々。

OPが試みた解決策は正しい方向に向かっており、@ hpk42の回答が示唆するMyTester.__init__ように、次のようなリクエストへの参照を保存することができます:

class MyTester(object):
    def __init__(self, request, arg=["var0", "var1"]):
        self.request = request
        self.arg = arg
        # self.use_arg_to_init_logging_part()

    def dothis(self):
        print "this"

    def dothat(self):
        print "that"

次に、これを使用して次のようなフィクスチャを実装します。

@pytest.fixture()
def tester(request):
    """ create tester object """
    # how to use the list below for arg?
    _tester = MyTester(request)
    return _tester

必要に応じて、個々のテストの動作を微調整するために、作成後にMyTesterその属性を更新できるように、クラスを少し再構築することができます。.args

于 2015-02-17T20:36:40.410 に答える
12

フィクスチャ関数から (したがって Tester クラスから) リクエスト元のモジュール/クラス/関数にアクセスできます。フィクスチャ関数からのテスト コンテキストのリクエストとの対話を参照してください。したがって、クラスまたはモジュールでいくつかのパラメーターを宣言すると、テスター フィクスチャがそれを取得できます。

于 2013-08-07T08:44:45.963 に答える