2012-09-13 7 views
24

Jenkins、Python、Selenium2(webdriver)、Py.testフレームワークを使用して、Webテストのテストケースを作成しています。Pytest:クラス内の残りのテストをスキップして失敗した場合の方法

これまでのところ、私は次のような構造で私のテストを整理しています:

クラステストケースであり、各test_方法テストステップです。

この設定は、すべてが正常に機能している場合には効果的ですが、1つのステップがクラッシュしたときに残りの「テストステップ」が狂ってしまいます。私はteardown_class()の助けを借りてクラス(テストケース)の中に失敗を含めることができますが、私はこれを改善する方法を検討しています。

test_メソッドのうちの1つが失敗した場合は、残りのテストケースが実行されずFAILEDとマークされるように、何とかスキップ(またはxfail)する必要があります。偽陽性)

ありがとう!

更新:私はそのように呼び出すことは非常に議論の余地があるので、それは悪い練習ではないと答えています。 (各テストクラスは独立していて、それで十分です)。

UPDATE 2:各テストメソッドに「if」条件を付けることはオプションではありません - 繰り返したくさんの作業があります。私が探しているのは(たぶん)誰かがクラスメソッドにhooksを使う方法を知っているということです。

+0

--maxfailフラグを1に設定することはできませんか? 1つのテストが失敗した場合、それはpy.testの終了になります。 – Nacht

+0

@Nacht他のテストケースが失敗したにもかかわらず、他のテストケースのテストを継続することですが、失敗したテストケースでテストステップを停止してください。 –

答えて

22

私は一般的な "テストステップ"のアイデアが好きです。私はそれを「インクリメンタル」テストと呼んでおり、機能テストのシナリオIMHOで最も理にかなっています。ここで

は(公式フック拡張子を除く)pytestの内部詳細に依存しない実装です:

import pytest 

def pytest_runtest_makereport(item, call): 
    if "incremental" in item.keywords: 
     if call.excinfo is not None: 
      parent = item.parent 
      parent._previousfailed = item 

def pytest_runtest_setup(item): 
    previousfailed = getattr(item.parent, "_previousfailed", None) 
    if previousfailed is not None: 
     pytest.xfail("previous test failed (%s)" %previousfailed.name) 

あなたは今、このような「test_step.py」をお持ちの場合:

import pytest 

@pytest.mark.incremental 
class TestUserHandling: 
    def test_login(self): 
     pass 
    def test_modification(self): 
     assert 0 
    def test_deletion(self): 
     pass 

は、それが(XFAILの理由を報告するために-rx使用して)次のようになります実行している:

(1)[email protected]:~/p/pytest/doc/en/example/teststep$ py.test -rx 
============================= test session starts ============================== 
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev17 
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout 
collected 3 items 

test_step.py .Fx 

=================================== FAILURES =================================== 
______________________ TestUserHandling.test_modification ______________________ 

self = <test_step.TestUserHandling instance at 0x1e0d9e0> 

    def test_modification(self): 
>  assert 0 
E  assert 0 

test_step.py:8: AssertionError 
=========================== short test summary info ============================ 
XFAIL test_step.py::TestUserHandling::()::test_deletion 
    reason: previous test failed (test_modification) 
================ 1 failed, 1 passed, 1 xfailed in 0.02 seconds ================= 

私はここで "xfail"を使用しています。なぜなら、間違った環境や間違ったインタプリタのバージョンのスキップがあるからです。

編集:あなたの例も私の例も、分散テストでは直接動作しないことに注意してください。このため、pytest-xdistプラグインは、クラスのテスト機能を通常は異なるスレーブに送信する現在のモードではなく、1つのテストスレーブに全面的に送信されるグループ/クラスを定義する方法を拡張する必要があります。

+0

ありがとうHolger。私はおそらく私の解決策でpy.testコードに深く入り込んでいることを知っていました。 :)私は間違いなくあなたの提案を試してみます。また、私はWeb UIの自動化のための機能テストケースを作成しています(最近、私は "テストステップ"の実装で "ホーネットの巣をかき混ぜる"ように感じているので、 "pytest-xdist"プラグインのために - 私はモジュールを配布していますが、まだ試していません。いずれにせよ、私は** Jenkins **を使ってテストを奴隷に配布することができます。 –

+0

また、各* step *はアプリケーションの状態をさらに変更するため、一般的なクラスのティアダウンは実際にはオプションではありません。だからこそ、私は漸進的にクラス "ティアダウン"を成長させようとしています。 (ここのようにhttp://stackoverflow.com/questions/12538808/pytest-2-3-adding-teardown-within-the-class/12573949#12573949)私はあなたがすでに回答を投稿していることを知っていますが、それが私の質問に答えるとは思わない。 :)私は自分の返信を投稿しました、あなたはそれについてコメントできますか?ありがとう –

+0

Holger私はちょうど私がこれを必要とする位置に自分自身を見つけたので、この投稿に出くわしました!これがプラグインまたはpytest自体の一部に組み込まれていれば素晴らしいでしょう:) –

4

あなたは何をしているのが一般的に悪い習慣です。それぞれのテストは、他のテストの結果に完全に依存しながら、他のテストとできるだけ独立している必要があります。

とにかく、the docsを読むと、あなたが望むような機能が実装されていないように思えます(おそらく役に立たないと思われたからです)。

この問題を回避するには、クラスにいくつかの条件を設定するカスタムメソッドを呼び出すテストを「不合格」、および「SKIPIF」デコレータで各テストをマークすることができます

あなたが行うことができます
class MyTestCase(unittest.TestCase): 
    skip_all = False 

    @pytest.mark.skipIf("MyTestCase.skip_all") 
    def test_A(self): 
     ... 
     if failed: 
      MyTestCase.skip_all = True 
    @pytest.mark.skipIf("MyTestCase.skip_all") 
    def test_B(self): 
     ... 
     if failed: 
      MyTestCase.skip_all = True 

か各テストを実行する前にこの制御を行い、最終的にpytest.skip()を呼び出します。

編集: xfailとしてマークすることは、同じ方法で行うことができますが、対応する関数呼び出しを使用して行うことができます。

おそらく、各テストのボイラープレートコードを書き換える代わりに、デコレータを書くことができます(おそらく、メソッドが失敗したかどうかを示す "フラグ"を返す必要があります)。

とにかく、私が指摘したいのは、これらのテストの1つが失敗した場合、同じテストケースで他の失敗したテストが誤ったポジティブと見なされるべきであるということです。 ハンド"。出力をチェックし、偽陽性を発見するだけです。 これは退屈かもしれませんが./error prone。

+0

ありがとうございます。たぶん私はそれを違う方法で見ているかもしれませんが、私はテストを完全に独立させるというカップルの状況に遭遇しました。それは意味がありません。例えば、私は "ユーザー"をテストします。テストケースは次のとおりです。1)ユーザーの作成2)ユーザーの変更3)ユーザーの削除。 "ユーザー作成"のテストケースでは、 "teardown" - ユーザーの削除 - を行う必要があります。これは、最初にもう一つのテストケースです。 "ユーザーの削除"テストケースを作成することは意味をなさない。しかし、これらの2つのテストケースを1つにまとめることは、テストケースという観点からは正しくありません。作成は成功するかもしれませんが、削除は失敗します。何をすべきか? –

+0

b)「時間がかかる」という前提条件のために実行できませんでした。いくつかのテストでは、特定の「状態」にあるPCが必要です。この「状態」はウェブサイトを通じて制御され、この状態に達するには2〜3時間が必要です。その後、私はこのPC状態で残りのテストを行います。この場合、テストケースごとにPCをこの状態に出し入れすることはできません。スイートが完了するまでには数週間かかるでしょう。 –

+2

これらの状況のほとんどは、モックを使用して回避することができます。 「コンピュータの状態」に依存したくない場合。あなたはちょうどモックを作成し、その状況でどのようにコード化反応をテストする必要があります。 これは不可能な場合があります。たとえば、RAMがほぼ満杯のときにプログラムがどのように動作するかをテストしたい場合などですが、これはユニットテストとはみなされないと思います。これらのテストは、グローバル状態とプログラム間の相互作用を制御します。 グローバル状態を完全に制御することも、_unit_テストを実行することもできません。 – Bakuriu

0

更新日: @ hpk42答えをご覧ください。彼の答えはあまり邪魔にならない。今

from _pytest.runner import runtestprotocol 
import pytest 
from _pytest.mark import MarkInfo 

def check_call_report(item, nextitem): 
    """ 
    if test method fails then mark the rest of the test methods as 'skip' 
    also if any of the methods is marked as 'pytest.mark.blocker' then 
    interrupt further testing 
    """ 
    reports = runtestprotocol(item, nextitem=nextitem) 
    for report in reports: 
     if report.when == "call": 
      if report.outcome == "failed": 
       for test_method in item.parent._collected[item.parent._collected.index(item):]: 
        test_method._request.applymarker(pytest.mark.skipif("True")) 
        if test_method.keywords.has_key('blocker') and isinstance(test_method.keywords.get('blocker'), MarkInfo): 
         item.session.shouldstop = "blocker issue has failed or was marked for skipping" 
      break 

def pytest_runtest_protocol(item, nextitem): 
# add to the hook 
    item.ihook.pytest_runtest_logstart(
     nodeid=item.nodeid, location=item.location, 
    ) 
    check_call_report(item, nextitem) 
    return True 

conftest.pyまたはは私の問題を解決しプラグインとしてこれを追加する:

これは私が実際に探していたものです。
blockerテストに失敗した場合は、テストを中止するように改善されました。 (これ以上のテストは役に立たないという意味です)

+0

あなたが提案して使うような「テストステップ」組織の有用性を知ることができます。実装はややハッキリです:)改善のために私の例を見てください。 – hpk42

0

または非常に単純代わりにCMD(またはTOXあるいはどこ)からpy.testを呼び出すだけ呼び出し:

py.test --maxfail=1 

は、複数のスイッチのためにここを参照してください: https://pytest.org/latest/usage.html

3
2

pytest-dependencyをご覧ください。他のテストが失敗した場合、いくつかのテストをスキップできるプラグインです。 あなたのケースでは、gbonettiが説明した増分テストがより適切であるようです。

関連する問題