7

私は現在、これらに似たプロジェクトとテストを行っています。

class mylib:
    @classmethod
    def get_a(cls):
        return 'a'

    @classmethod
    def convert_a_to_b(cls, a):
        return 'b'

    @classmethod
    def works_with(cls, a, b):
        return True

class TestMyStuff(object):
    def test_first(self):
        self.a = mylib.get_a()

    def test_conversion(self):
        self.b = mylib.convert_a_to_b(self.a)

    def test_a_works_with_b(self):
        assert mylib.works_with(self.a, self.b)

py.test 0.9.2では、これらのテスト(または同様のテスト)に合格します。それ以降のバージョンのpy.testでは、test_conversionとtest_a_works_with_bは「TestMyStuffには属性aがありません」で失敗します。

これは、後のpy.testのビルドで、テストされるメソッドごとにTestMyStuffの個別のインスタンスが作成されるためだと思います。

シーケンスの各ステップで結果が得られるように、これらのテストを作成する適切な方法は何ですか。ただし、前の(成功した)テストの状態を使用して、後続のテストを実行できますか(必須)。

4

5 に答える 5

10

テスト状態のランダムな共有を避けるのが良いという点で、Ned に部分的に同意します。しかし、テスト中に段階的に状態を蓄積することが役立つ場合もあると思います。

py.test を使用すると、テスト状態を共有したいことを明示的にすることで、実際にそれを行うことができます。あなたの例は動作するように書き直されました:

class State:
    """ holding (incremental) test state """

def pytest_funcarg__state(request):
    return request.cached_setup(
        setup=lambda: State(),
        scope="module"
    )

class mylib:
    @classmethod
    def get_a(cls):
        return 'a'

    @classmethod
    def convert_a_to_b(cls, a):
        return 'b'

    @classmethod
    def works_with(cls, a, b):
        return True

class TestMyStuff(object):
    def test_first(self, state):
        state.a = mylib.get_a()

    def test_conversion(self, state):
        state.b = mylib.convert_a_to_b(state.a)

    def test_a_works_with_b(self, state):
        mylib.works_with(state.a, state.b)

これは、最近の py.test バージョンで実行できます。各関数は「状態」オブジェクトを受け取り、「funcarg」ファクトリが最初にそれを作成し、モジュール スコープでキャッシュします。テストがファイル順に実行されることを保証する py.test とともに、テス​​ト関数は、テストの「状態」に対して段階的に動作するようになります。

ただし、「py.test -k test_conversion」などで「test_conversion」の実行のみを選択すると、最初のテストが実行されていないためにテストが失敗するため、少し壊れやすいです。インクリメンタル テストを行う何らかの方法があればよいので、最終的には完全に堅牢なソリューションを見つけることができると思います。

HTH、ホルガー

于 2010-07-15T08:55:45.610 に答える
9

単体テストの適切な方法は、テスト間で蓄積された状態を回避することです。ほとんどの単体テストフレームワークは、状態が蓄積するのを防ぐために非常に長い時間を費やしています。その理由は、各テストを独立させたいからです。これにより、テストの任意のサブセットを実行でき、各テストでシステムがクリーンな状態にあることが保証されます。

于 2010-06-30T00:35:24.993 に答える
1

この問題に多くの時間を費やすにつれて、特定するのを怠った私の質問に暗黙の側面があることに気付きました。ほとんどのシナリオでは、1 つのクラス内に状態を蓄積し、テスト クラスが完了したら破棄する必要があることがわかりました。

クラス自体が状態を蓄積するプロセスを表すいくつかのクラスで最終的に使用したものは、蓄積された状態をクラスオブジェクト自体に保存しました。

class mylib:
    @classmethod
    def get_a(cls):
        return 'a'

    @classmethod
    def convert_a_to_b(cls, a):
        return 'b'

    @classmethod
    def works_with(cls, a, b):
        return True

class TestMyStuff(object):
    def test_first(self):
        self.__class__.a = mylib.get_a()

    def test_conversion(self):
        self.__class__.b = mylib.convert_a_to_b(self.a)

    def test_a_works_with_b(self):
        mylib.works_with(self.a, self.b)

このアプローチの利点は、テスト クラス内にカプセル化された状態を保持することです (テストを実行するために存在する必要がある補助関数はありません)。また、別のクラスが TestMyStuff 状態が別のクラスが実行されるときに表示されます。

これまでに説明したこれらのアプローチにはそれぞれメリットがあると思います。それぞれのアプローチが最適な場合に使用するつもりです。

于 2010-07-15T13:04:28.487 に答える