PythonでPyTestのassertステートメントの動作を変更することは可能ですか


18

私はPythonのassertステートメントを使用して、実際の動作と予想される動作を一致させています。エラーテストケースがある場合のように、これらを制御することはできません。アサーションエラーを制御し、アサートの失敗時にテストケースを中止するかどうかを定義したい。

また、アサーションエラーが発生した場合など、テストケースを一時停止してユーザーがいつでも再開できるようにすることもできます。

私はこれを行う方法がわかりません

コード例、ここではpytestを使用しています

import pytest
def test_abc():
    a = 10
    assert a == 10, "some error message"

Below is my expectation

assertがassertionErrorをスローする場合、テストケースを一時停止するオプションが必要で、デバッグして後で再開できます。一時停止と再開には、tkinterモジュールを使用します。以下のようにアサート機能を作成します

import tkinter
import tkinter.messagebox

top = tkinter.Tk()

def _assertCustom(assert_statement, pause_on_fail = 0):
    #assert_statement will be something like: assert a == 10, "Some error"
    #pause_on_fail will be derived from global file where I can change it on runtime
    if pause_on_fail == 1:
        try:
            eval(assert_statement)
        except AssertionError as e:
            tkinter.messagebox.showinfo(e)
            eval (assert_statement)
            #Above is to raise the assertion error again to fail the testcase
    else:
        eval (assert_statement)

今後は、この関数を使用してすべてのassertステートメントを変更する必要があります

import pytest
def test_abc():
    a = 10
    # Suppose some code and below is the assert statement 
    _assertCustom("assert a == 10, 'error message'")

私がアサートを使用した何千もの場所で変更を行わなければならないので、これは私にとってあまりにも多くの努力です。それを行う簡単な方法はありますかpytest

Summary:失敗時にテストケースを一時停止し、デバッグ後に再開できるものが必要です。私はそれを知ってtkinterおり、それが私がそれを使用した理由です。他のアイデアは歓迎されます

Note:上記のコードはまだテストされていません。小さな構文エラーもあるかもしれません

編集:回答をありがとう。この質問を少し先に拡張します。アサートの動作を変更したい場合はどうすればよいですか。現在、アサーションエラーがある場合、テストケースが終了します。特定のアサートエラーでテストケースの終了が必要かどうかを選択したい場合はどうすればよいですか。上記のようにカスタムアサート関数を記述したくないのは、この方法で多くの場所で変更する必要があるためです。


3
やりたいことのコード例を教えてください。
mrblewog '10 / 10/19

1
使用せずにassert、必要なことを行う独自のチェック関数を作成してください。
molbdnilo 2019年

tryブロックにassertを挿入exceptにエラーメッセージを挿入しないのはなぜですか?
Prathik Kini、

1
pytestテストケースで実際に使用したいようです。テストスイートの作成を容易にする多くの機能と共に、アサートテストスキップテストの使用をサポートしています。
blubberdiblub

1
assert cond, "msg"コード内のすべてを機械的に置き換えるシンプルなツールを書くのはかなり簡単ではないでしょう_assertCustom("assert cond, 'msg'")か?たぶん、sedワンライナーでそれができるでしょう。
NPE、

回答:


23

を使用しているためpytest、失敗したテストと対話するための十分なオプションが提供されます。コマンドラインオプションと、これを可能にするいくつかのフックを提供します。それぞれの使用方法と、特定のデバッグのニーズに合わせてカスタマイズできる場所について説明します。

また、本当に必要な場合は、特定のアサーションを完全にスキップできる、よりエキゾチックなオプションについても説明します。

アサートではなく例外を処理する

失敗したテストは通常​​pytestを停止しないことに注意してください。明示的に有効にして、特定の数の失敗後に終了するように明示的に指示した場合のみ。また、例外が発生するため、テストは失敗します。assertAssertionError発生しますが、テストが失敗する唯一の例外ではありません!変更ではなく、例外の処理方法を制御したいassert

ただし、アサート失敗する、個々のテスト終了します。これは、try...exceptブロックの外で例外が発生すると、Pythonが現在の関数フレームを巻き戻し、それ以上戻ることがないためです。

_assertCustom()アサーションを再実行する試みの説明から判断すると、それはあなたが望んでいることではないと思いますが、それでも、オプションについては後で詳しく説明します。

pdbを使用したpytestでの事後デバッグ

デバッガーでの失敗を処理するためのさまざまなオプションについては、テストが失敗したときに標準のデバッグプロンプトを開く--pdbコマンドラインスイッチから始めます(簡潔にするために出力を省略しています)。

$ mkdir demo
$ touch demo/__init__.py
$ cat << EOF > demo/test_foo.py
> def test_ham():
>     assert 42 == 17
> def test_spam():
>     int("Vikings")
> EOF
$ pytest demo/test_foo.py --pdb
[ ... ]
test_foo.py:2: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /.../demo/test_foo.py(2)test_ham()
-> assert 42 == 17
(Pdb) q
Exit: Quitting debugger
[ ... ]

このスイッチを使用すると、テストが失敗するとpytestが事後デバッグセッションを開始します。これは基本的にはまさにあなたが望んでいたものです。テストが失敗した時点でコードを停止し、デバッガーを開いてテストの状態を確認します。テストのローカル変数、グローバル、およびスタック内のすべてのフレームのローカルとグローバルを操作できます。

ここでpytestは、この時点で終了するかどうかを完全に制御しますq。quitコマンドを使用すると、pytestは実行も終了します。forcontinue を使用するcと、制御がpytestに戻り、次のテストが実行されます。

代替デバッガーの使用

このため、pdbデバッガーに拘束されません。--pdbclsスイッチで別のデバッガーを設定できます。IPythonデバッガーの実装、または他のほとんどのPythonデバッガーpudbデバッガーでは、スイッチを使用する必要があるか、特別なプラグイン)を含め、pdb.Pdb()互換性のある任意の実装が機能します。スイッチはモジュールとクラスを受け取ります。たとえば、次のように使用できます。-spudb

$ pytest -s --pdb --pdbcls=pudb.debugger:Debugger

あなたは周り独自のラッパークラスを書くためにこの機能を使用することができますPdbことは、単にすぐに戻り、特定の障害はあなたが興味のあるものではありません場合。pytest用途をPdb()正確に似pdb.post_mortem()てい

p = Pdb()
p.reset()
p.interaction(None, t)

ここで、tあるトレースバックオブジェクトがp.interaction(None, t)戻ったとき、pytestが(その時点でpytestが終了する)に設定されていない限り 、次のテストを続行します。p.quittingTrue

以下は、テストが発生しない限り、デバッグを拒否してすぐに戻ることを出力しValueError、次のように保存した実装例demo/custom_pdb.pyです。

import pdb, sys

class CustomPdb(pdb.Pdb):
    def interaction(self, frame, traceback):
        if sys.last_type is not None and not issubclass(sys.last_type, ValueError):
            print("Sorry, not interested in this failure")
            return
        return super().interaction(frame, traceback)

これを上記のデモで使用すると、出力されます(ここでも簡潔にするために省略しています)。

$ pytest test_foo.py -s --pdb --pdbcls=demo.custom_pdb:CustomPdb
[ ... ]
    def test_ham():
>       assert 42 == 17
E       assert 42 == 17

test_foo.py:2: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Sorry, not interested in this failure
F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    def test_spam():
>       int("Vikings")
E       ValueError: invalid literal for int() with base 10: 'Vikings'

test_foo.py:4: ValueError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /.../test_foo.py(4)test_spam()
-> int("Vikings")
(Pdb)

上記のイントロスペクトsys.last_typeは、障害が「興味深い」かどうかを判断します。

ただし、tkInterまたは同様の何かを使用して独自のデバッガーを作成する場合を除いて、このオプションを実際にお勧めすることはできません。これは大きな仕事であることに注意してください。

フィルタリングの失敗; デバッガーを開くタイミングを選択します

次のレベルはpytestのデバッグと相互作用のフックです。これらは、動作のカスタマイズのフックポイントであり、pytestが例外の処理や、pdb.set_trace()またはbreakpoint()(Python 3.7以降)を介してデバッガーに入るなどの通常の処理方法を置換または強化します。

このフックの内部実装は>>> entering PDB >>>上記のバナーの印刷も担当するため、このフックを使用してデバッガーが実行されないようにすると、この出力はまったく表示されなくなります。独自のフックを作成して、テストの失敗が「興味深い」ときに元のフックに委任することができるため、使用しているデバッガーとは無関係にテストの失敗をフィルター処理できます。名前でアクセスすることにより、内部実装にアクセスできます。このための内部フックプラグインはという名前pdbinvokeです。実行されないようにするには、登録解除する必要がありますが、参照を保存する必要があります。必要に応じて直接呼び出すことができます。

このようなフックの実装例を以下に示します。これは、プラグインが読み込まれる任意の場所に配置できます。私はそれを入れましたdemo/conftest.py

import pytest

@pytest.hookimpl(trylast=True)
def pytest_configure(config):
    # unregister returns the unregistered plugin
    pdbinvoke = config.pluginmanager.unregister(name="pdbinvoke")
    if pdbinvoke is None:
        # no --pdb switch used, no debugging requested
        return
    # get the terminalreporter too, to write to the console
    tr = config.pluginmanager.getplugin("terminalreporter")
    # create or own plugin
    plugin = ExceptionFilter(pdbinvoke, tr)

    # register our plugin, pytest will then start calling our plugin hooks
    config.pluginmanager.register(plugin, "exception_filter")

class ExceptionFilter:
    def __init__(self, pdbinvoke, terminalreporter):
        # provide the same functionality as pdbinvoke
        self.pytest_internalerror = pdbinvoke.pytest_internalerror
        self.orig_exception_interact = pdbinvoke.pytest_exception_interact
        self.tr = terminalreporter

    def pytest_exception_interact(self, node, call, report):
        if not call.excinfo. errisinstance(ValueError):
            self.tr.write_line("Sorry, not interested!")
            return
        return self.orig_exception_interact(node, call, report)

上記のプラグインは、内部TerminalReporterプラグインを使用して端末に行を書き込みます。これにより、デフォルトのコンパクトテストステータス形式を使用するときに出力がよりクリーンになり、出力キャプチャが有効になっていても、端末に物を書き込むことができます。

この例でpytest_exception_interactpytest_configure()、別のフックを介してプラグインオブジェクトをフックに登録していますが@pytest.hookimpl(trylast=True)、内部pdbinvokeプラグインの登録を解除できるように(を使用して)十分に遅く実行されていることを確認してください。フックが呼び出されると、サンプルはcall.exceptinfoオブジェクトに対してテストしますノードまたはレポートも確認できます。

上記のサンプルコードをに配置するdemo/conftest.pyと、test_hamテストエラーは無視され、test_spamテストエラーのみが発生ValueErrorし、デバッグプロンプトが開きます。

$ pytest demo/test_foo.py --pdb
[ ... ]
demo/test_foo.py F
Sorry, not interested!

demo/test_foo.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    def test_spam():
>       int("Vikings")
E       ValueError: invalid literal for int() with base 10: 'Vikings'

demo/test_foo.py:4: ValueError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /.../demo/test_foo.py(4)test_spam()
-> int("Vikings")
(Pdb) 

繰り返しますが、上記のアプローチには、これをputestを含むpytestで動作するデバッガーまたはIPythonデバッガーと組み合わせることができるという追加の利点があります。

$ pytest demo/test_foo.py --pdb --pdbcls=IPython.core.debugger:Pdb
[ ... ]
demo/test_foo.py F
Sorry, not interested!

demo/test_foo.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    def test_spam():
>       int("Vikings")
E       ValueError: invalid literal for int() with base 10: 'Vikings'

demo/test_foo.py:4: ValueError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /.../demo/test_foo.py(4)test_spam()
      1 def test_ham():
      2     assert 42 == 17
      3 def test_spam():
----> 4     int("Vikings")

ipdb>

また、(node引数を介して)どのテストが実行されたかについてのコンテキストが多くなり、(call.excinfo ExceptionInfoインスタンスを介して)発生した例外に直接アクセスできます。

特定のpytestデバッガプラグイン(pytest-pudbまたはなどpytest-pycharm)は独自のpytest_exception_interactフックスプを登録することに注意してください。より完全な実装は、任意のプラグインを自動的にオーバーライドして使用しconfig.pluginmanager.list_name_pluginhasattr()各プラグインをテストするために、plugin-manager内のすべてのプラグインをループする必要があります。

失敗を完全になくす

これにより、失敗したテストのデバッグを完全に制御できますが、特定のテストでデバッガーを開かないことを選択した場合でも、テストは失敗したままになります。失敗を完全に解消したい場合は、別のフックを使用できますpytest_runtest_call()

pytestがテストを実行する場合、上記のフックを介してテストを実行します。フックNoneは例外を返すか、発生させることが期待されています。これからレポートが作成され、オプションでログエントリが作成さpytest_exception_interact()れます。テストが失敗した場合は、前述のフックが呼び出されます。したがって、このフックが生成する結果を変更するだけで済みます。例外の代わりに、何も返すべきではありません。

これを行う最良の方法は、フックラッパーを使用することです。フックラッパーは実際の作業を行う必要はありませんが、代わりにフックの結果に何が起こるかを変更する機会が与えられます。次の行を追加するだけです。

outcome = yield

あなたのフックラッパー実装で、あなたは経由でテスト例外を含むフック結果にアクセスできますoutcome.excinfo。テストで例外が発生した場合、この属性は(タイプ、インスタンス、トレースバック)のタプルに設定されます。または、outcome.get_result()標準のtry...except処理を呼び出して使用することもできます。

では、どのようにして失敗したテストに合格するのですか?3つの基本オプションがあります。

  • ラッパーを呼び出すことで、テストを予期される失敗としてマークできpytest.xfail()ます。
  • を呼び出すことにより、最初にテストが実行されなかったふりをして、項目をスキップ済みとしてマークできpytest.skip()ます。
  • outcome.force_result()メソッドを使用して、例外を削除できます。ここで結果を空のリストに設定します(つまり、登録されたフックは以外に何も生成しませんでしたNone)。例外は完全にクリアされます。

何を使うかはあなた次第です。テストが失敗したかのようにそれらのケースを処理する必要がないため、スキップされたテストと予期された失敗のテストの結果を必ず確認してください。あなたはこれらのオプションを経由して上げる特殊な例外にアクセスすることができますpytest.skip.Exceptionし、pytest.xfail.Exception

以下は、raiseを行わない失敗したテストをスキップ済みValueErrorとしてマークする実装例です。

import pytest

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(item):
    outcome = yield
    try:
        outcome.get_result()
    except (pytest.xfail.Exception, pytest.skip.Exception, pytest.exit.Exception):
        raise  # already xfailed,  skipped or explicit exit
    except ValueError:
        raise  # not ignoring
    except (pytest.fail.Exception, Exception):
        # turn everything else into a skip
        pytest.skip("[NOTRUN] ignoring everything but ValueError")

conftest.py出力に入れると次のようになります:

$ pytest -r a demo/test_foo.py
============================= test session starts =============================
platform darwin -- Python 3.8.0, pytest-3.10.0, py-1.7.0, pluggy-0.8.0
rootdir: ..., inifile:
collected 2 items

demo/test_foo.py sF                                                      [100%]

=================================== FAILURES ===================================
__________________________________ test_spam ___________________________________

    def test_spam():
>       int("Vikings")
E       ValueError: invalid literal for int() with base 10: 'Vikings'

demo/test_foo.py:4: ValueError
=========================== short test summary info ============================
FAIL demo/test_foo.py::test_spam
SKIP [1] .../demo/conftest.py:12: [NOTRUN] ignoring everything but ValueError
===================== 1 failed, 1 skipped in 0.07 seconds ======================

-r aフラグを使用して、test_ham今はスキップされたことを明確にしました。

pytest.skip()呼び出しをpytest.xfail("[XFAIL] ignoring everything but ValueError")に置き換えると、テストは予期される失敗としてマークされます。

[ ... ]
XFAIL demo/test_foo.py::test_ham
  reason: [XFAIL] ignoring everything but ValueError
[ ... ]

使用するとoutcome.force_result([])マークされます:

$ pytest -v demo/test_foo.py  # verbose to see individual PASSED entries
[ ... ]
demo/test_foo.py::test_ham PASSED                                        [ 50%]

ユースケースに最も適していると思うのはあなた次第です。以下のためにskip()xfail()、私は、標準のメッセージ形式を模倣(接頭辞[NOTRUN]または[XFAIL])しかし、あなたが望む任意の他のメッセージ・フォーマットを自由に使用できます。

3つのケースすべてで、pytestは、このメソッドを使用して結果を変更したテストのデバッガーを開きません。

個々のアサートステートメントの変更

assertテスト内のテストを変更する場合は、さらに多くの作業を行うように設定します。はい、これは技術的には可能ですが、Pythonがコンパイル時に実行するコードそのものを書き直すことによってのみ可能です。

を使用する場合pytest、これは実際にはすでに行われています。Pytest assertステートメントを書き換えて、アサートが失敗したときにコンテキストを提供します。何が行われているのかについての概要とソースコードについては、このブログ投稿を参照してください。このモジュールは1k行を超えるため、Pythonの抽象構文ツリーがどのように機能するかを理解する必要があります。そうした場合、あなたは可能性があり、周囲を含め、そこに独自の変更を加えるためにそのモジュールをモンキーパッチとハンドラ。_pytest/assertion/rewrite.pyasserttry...except AssertionError:

ただし、アサートを選択的に無効化または無視することはできません。後続のステートメントは、スキップされたアサートが保護することを意図した状態(特定のオブジェクト配置、変数セットなど)に簡単に依存する可能性があるためです。アサートテスト場合はfooされていないNone、その後アサートはに依存してfoo.bar存在するように、あなたは単にに実行されますAttributeErrorあなたはこのルートを行くために必要がある場合は、再調達例外になど、そこドゥスティックを。

assertsここで書き直すことについてこれ以上詳しく説明するつもりはありません。これは、関係する作業量を考慮せずに、これを実行する価値はないと思います。また、事後デバッグにより、テストの状態にアクセスできます。とにかくアサーションエラーのポイント。

これを実行したい場合は、使用する必要はありませんeval()(いずれにしても機能しないassertため、ステートメントなのでexec()、代わりに使用する必要があります)。また、アサーションを2回実行する必要もないことに注意してください。アサーションで使用されている式が状態を変更した場合、問題が発生する可能性があります)。代わりに、ast.Assertノードをノード内に埋め込み、ast.Try空のast.Raiseノードを使用する例外ハンドラをアタッチして、キャッチされた例外を再発生させます。

デバッガーを使用してアサーションステートメントをスキップします。

Pythonデバッガでは、実際に/ コマンドを使用してステートメントスキップできます。特定のアサーション失敗すること前もってわかっている場合は、これを使用してバイパスできます。すべてのテストの開始時にデバッガーを開くを使用してテストを実行し、デバッガーがアサートの直前で一時停止しているときにa を発行してスキップすることができます。jjump--tracej <line after assert>

これを自動化することもできます。上記のテクニックを使用して、カスタムデバッガプラグインを構築できます。

  • pytest_testrun_call()フックを使用してAssertionError例外をキャッチします
  • トレースバックから「問題のある」行番号を抽出し、おそらくいくつかのソースコード分析により、ジャンプの実行に必要なアサーションの前後の行番号を決定します
  • はテストを再度実行しますが、今回Pdbはアサートの前の行にブレークポイントを設定し、ブレークポイントに到達すると2番目にジャンプしてからc継続するサブクラスを使用します。

または、アサーションが失敗するのを待つ代わりassertに、テストで見つかったそれぞれのブレークポイントの設定を自動化できます(ソースコード分析を使用して、テストast.AssertのASTのノードの行番号を簡単に抽出できます)。アサートされたテストを実行します。デバッガスクリプトコマンドを使用し、jumpコマンドを使用してアサーション自体をスキップします。あなたはトレードオフをする必要があるでしょう。デバッガーの下ですべてのテストを実行します(インタープリターはすべてのステートメントに対してトレース関数を呼び出す必要があるため、処理が遅くなります)。または、これを失敗したテストにのみ適用し、それらのテストを最初から再実行する代償を払ってください。

このようなプラグインは作成するのに多くの作業が必要になるため、ここでは例を記述しません。これは、とにかくそれが回答に収まらないため、また、時間の価値がないと思うためです。デバッガーを開いて手動でジャンプします。アサートの失敗は、テスト自体またはテスト中のコードのいずれかにバグがあることを示しているため、問題のデバッグに集中することもできます。


7

pytest --pdbを使用すると、コードをまったく変更しなくても、希望どおりの結果を得ることができます。

あなたの例で:

import pytest
def test_abc():
    a = 9
    assert a == 10, "some error message"

--pdbで実行:

py.test --pdb
collected 1 item

test_abc.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    def test_abc():
        a = 9
>       assert a == 10, "some error message"
E       AssertionError: some error message
E       assert 9 == 10

test_abc.py:4: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /private/tmp/a/test_abc.py(4)test_abc()
-> assert a == 10, "some error message"
(Pdb) p a
9
(Pdb)

テストが失敗するとすぐに、組み込みのPythonデバッガーでデバッグできます。デバッグが完了したらcontinue、残りのテストを実行できます。


テストケースが失敗した場合、またはテストステップが失敗した場合、これは停止しますか。
Nitesh

リンクドキュメントを確認してください:doc.pytest.org/en/latest/...
gnvk

素晴らしいアイデア。ただし、-pdbを使用すると、失敗するたびにテストケースが一時停止します。失敗時にテストケースを一時停止するかどうかを実行時に決定できます
Nitesh

5

PyCharmを使用している場合は、例外ブレークポイントを追加して、アサートが失敗したときに実行を一時停止できます。ブレークポイントの表示(CTRL-SHIFT-F8)を選択し、AssertionErrorのオンレイズ例外ハンドラーを追加します。これにより、テストの実行が遅くなる可能性があることに注意してください。

それ以外の場合、アサーションが失敗した時点ではなく、失敗した各テストの最後(エラーが発生する直前)で一時停止してもかまわない場合は、いくつかのオプションがあります。ただし、この時点で、テストで開かれたファイルを閉じるなど、さまざまなクリーンアップコードがすでに実行されている可能性があることに注意してください。可能なオプションは次のとおりです。

  1. --pdbオプションを使用して、エラーが発生したときにデバッガにドロップするようにpytestに指示できます

  2. 次のデコレータを定義して、関連する各テスト関数をそれでデコレートできます。(メッセージのログ記録は別として、この回答で説明されているように、この時点でpdb.post_mortemを開始したり、例外が発生したフレームのローカルとインタラクティブなcode.interact実行したりすることもできます。)

from functools import wraps

def pause_on_assert(test_func):
    @wraps(test_func)
    def test_wrapper(*args, **kwargs):
        try:
            test_func(*args, **kwargs)
        except AssertionError as e:
            tkinter.messagebox.showinfo(e)
            # re-raise exception to make the test fail
            raise
    return test_wrapper

@pause_on_assert
def test_abc()
    a = 10
    assert a == 2, "some error message"
  1. 手動ですべてのテスト関数を飾るためにしたくない場合は、代わりにautouse治具検査その定義できsys.last_valueを
import sys

@pytest.fixture(scope="function", autouse=True)
def pause_on_assert():
    yield
    if hasattr(sys, 'last_value') and isinstance(sys.last_value, AssertionError):
        tkinter.messagebox.showinfo(sys.last_value)

私はデコレータでの答えが好きでしたが、動的に行うことはできません。pause_on_assertを実行するかどうかを動的に制御したい。これに対する解決策はありますか?
Nitesh

どのように動的ですか?どこでもそれを有効/無効にする単一のスイッチのように?または、各テストでそれを制御する方法はありますか?
Uri Granta、

いくつかのテストケースを実行しているとします。途中で、失敗時に一時停止する必要が出てきました。スイッチを有効にします。後でいつでも、スイッチを無効にする必要があると感じます。
Nitesh

私のテストケースには複数のアサートがあるため、answer:2のデコレーターは機能しません
Nitesh

「スイッチ」に関しては、pause_on_assert一時停止するかどうかを決定するためにファイルから読み取るようにの実装を更新できます。
Uri Granta、

4

簡単な解決策の1つは、Visual Studio Codeを使用する場合、条件付きブレークポイントを使用することです。

これにより、たとえば次のようにアサーションを設定できます。

import pytest
def test_abc():
    a = 10
    assert a == 10, "some error message"

次に、アサーション行に条件付きブレークポイントを追加します。これは、アサーションが失敗したときにのみブレークします。

ここに画像の説明を入力してください


@Nitesh-このソリューションはすべての問題を解決すると思います。アサーションが失敗したときにのみ中断し、そこでコードをデバッグして、その後残りのテストを続行できます...セットアップは少し面倒ですが当初
Nick Martin
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.