[python] 파이썬에서 PyTest의 assert 문 동작을 변경할 수 있습니까?

실제 및 예상 동작과 일치시키기 위해 Python assert 문을 사용하고 있습니다. 오류 테스트 사례가 중단 된 것처럼이를 제어 할 수 없습니다. 어설 션 오류를 제어하고 실패 주장에 대한 테스트 사례를 중단할지 여부를 정의하고 싶습니다.

또한 어설 션 오류가있는 경우 테스트 케이스를 일시 중지하고 사용자가 언제든지 다시 시작할 수있는 것과 같은 것을 추가하고 싶습니다.

나는 이것을하는 방법을 모른다

코드 예, 여기서는 pytest를 사용하고 있습니다

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

Below is my expectation

assert가 assertionError를 던지면 테스트 케이스를 일시 중지하는 옵션이 있어야하며 나중에 디버그하고 다시 시작할 수 있습니다. 일시 중지 및 재개를 위해 tkinter모듈 을 사용 합니다. 아래와 같이 assert 함수를 만들 것입니다

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: 위의 코드는 아직 테스트되지 않았습니다. 작은 구문 오류도있을 수 있습니다

편집 : 답변 주셔서 감사합니다. 이 질문을 조금 더 앞당기십시오. assert의 동작을 변경하려면 어떻게해야합니까? 현재 어설 션 오류 테스트 케이스가 종료 된 경우 특정 어설 션 실패에서 테스트 케이스 종료가 필요한지 여부를 선택하려면 어떻게해야합니까? 이 방법으로 여러 위치에서 변경해야하기 때문에 위에서 언급 한대로 사용자 정의 어설 션 함수를 작성하고 싶지 않습니다.



답변

pytest실패한 테스트와 상호 작용할 수있는 충분한 옵션을 제공하는을 사용 하고 있습니다. 명령 행 옵션과이를 가능하게하는 몇 가지 후크를 제공합니다. 각각의 사용법과 특정 디버깅 요구에 맞게 사용자 정의 할 수있는 위치에 대해 설명합니다.

또한 정말로 필요한 경우 특정 어설 션을 완전히 건너 뛸 수있는보다 이국적인 옵션을 살펴 보겠습니다.

단언이 아닌 예외 처리

실패한 테스트는 일반적으로 pytest를 중지하지 않습니다. 특정 횟수의 실패 후에 명시 적으로 종료하도록 지시 한 경우에만 가능합니다 . 또한 예외가 발생하여 테스트가 실패합니다. assert제기 AssertionError하지만 테스트를 실패하게 만드는 유일한 예외는 아닙니다! alter가 아닌 예외 처리 방법을 제어하려고합니다 assert.

그러나 어설 션 실패 하면 개별 테스트 종료 됩니다 . try...except블록 외부에서 예외가 발생 하면 파이썬은 현재 함수 프레임을 풀고 다시 돌아 가지 않기 때문입니다.

_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는이 시점 이후에 종료할지 여부를 완전히 제어합니다. qquit 명령을 사용하면 pytest도 실행을 종료하고 cfor continue는 제어를 pytest로 리턴하고 다음 테스트가 실행됩니다.

대체 디버거 사용

이를 위해 pdb디버거에 바인딩되지 않았습니다 . --pdbcls스위치 로 다른 디버거를 설정할 수 있습니다 . IPython 디버거 구현 또는 대부분의 다른 Python 디버거를 포함하여 pdb.Pdb()호환 가능한 모든 구현이 작동 합니다 ( pudb 디버거 에는 스위치 사용 또는 특수 플러그인 필요 ). 스위치는 모듈과 클래스를 사용 합니다. 예를 들어 다음을 사용할 수 있습니다.-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_interact다른 hook을 통해 플러그인 객체를 hook 에 등록하지만 내부 플러그인의 등록을 취소 할 수있을 pytest_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) 

다시 말하면 , 위의 접근 방식은 pudb 또는 IPython 디버거를 포함하여 pytest 와 함께 작동하는 디버거 와 이것을 결합 할 수 있다는 추가 이점이 있습니다 .

$ 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_interacthooksp를 등록합니다 . 보다 완전한 구현은 플러그인 관리자의 모든 플러그인을 반복하여 임의의 플러그인을 자동으로 재정의 config.pluginmanager.list_name_plugin하고hasattr() 각 플러그인을 테스트하기 위해 .

실패를 완전히 없애기

이렇게하면 실패한 테스트 디버깅을 완전히 제어 할 수 있지만 주어진 테스트에 대해 디버거를 열지 않더라도 테스트는 실패한 것으로 남아 있습니다. 실패를 완전히 없애고 싶다면 다른 후크를 사용할 수 있습니다 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.

다음 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%]

유스 케이스에 가장 적합한 것은 당신에게 달려 있습니다. For skip()xfail()표준 메시지 형식 ( [NOTRUN]또는 접두사)을 흉내 [XFAIL]냈지만 원하는 다른 메시지 형식을 자유롭게 사용할 수 있습니다.

세 가지 경우 모두 pytest는이 방법을 사용하여 결과를 변경 한 테스트의 디버거를 열지 않습니다.

개별 주장 진술 변경

assert테스트 내에서 테스트 를 변경 하려면 훨씬 더 많은 작업을 수행해야합니다. 예, 이것은 기술적으로 가능하지만 컴파일 타임에 Python이 실행할 코드를 다시 작성해야만 가능합니다 .

당신이 사용할 때 pytest, 이것은 실제로 이미 수행되고 있습니다. Pytest 는 어설 션이 실패 할 때 더 많은 컨텍스트를 제공하기 위해 명령문을 다시 작성 assert합니다 . 소스 코드 뿐만 아니라 수행중인 작업에 대한 개요를 보려면 이 블로그 게시물 을 참조하십시오 . 이 모듈의 길이는 1k 줄 이상이므로 Python의 추상 구문 트리 작동 방식을 이해해야합니다 . 당신이 할 경우, 당신은 할 수 주변을 포함하여, 거기에 자신 만의 수정을 추가 할 수있는 해당 모듈 monkeypatch 와 핸들러를._pytest/assertion/rewrite.pyasserttry...except AssertionError:

그러나 후속 명령문은 건너 뛴 어설 션이 보호해야하는 상태 (특정 오브젝트 배열, 변수 세트 등)에 쉽게 종속 될 수 있으므로 어설 션을 선택적으로 비활성화하거나 무시할 수 없습니다. 어설 테스트하면 foo되지 None, 다음 나중에 어설에 의존하는 foo.bar존재, 당신은 단순히으로 실행 AttributeError이 경로를 이동해야하는 경우, 다시 제기 예외 등, 거기에서 스틱을.

필자는 asserts여기 에 재 작성에 대해 자세히 설명 하지 않을 것입니다. 이것은 관련된 작업량을 고려하지 않고 추구 할 가치가 있다고 생각하지 않기 때문에 사후 디버깅을 통해 테스트 상태에 액세스 할 수 있습니다. 어쨌든 어설 션 실패 지점 .

이 작업을 원한다면 사용할 필요가 없습니다 eval()(어쨌든 작동하지 않을 것입니다, assert진술이므로 exec()대신 사용해야 합니다). 어설 션을 두 번 실행해야합니다 ( 어설 션에 사용 된식이 상태를 변경하면 문제가 발생할 수 있습니다). 대신 ast.Assert노드를 노드 안에 임베드하고 ast.Tryast.Raise노드 를 사용하는 예외 처리기를 연결하여 포착 된 예외를 다시 발생시킵니다.

디버거를 사용하여 어설 션 문을 건너 뜁니다.

Python 디버거를 사용하면 실제로 / 명령을 사용하여 명령문건너 뛸 수 있습니다 . 당신이 알고있는 경우 앞까지 특정 주장이 그 것이다 실패, 당신은 바이 패스이를 사용할 수 있습니다. 을 사용하여 테스트를 실행할 수 있습니다. 그러면 모든 테스트 시작시 디버거가 열리고 어설 션 직전에 디버거가 일시 중지되면 a 를 실행하여 건너 뜁니다.jjump--tracej <line after assert>

이것을 자동화 할 수도 있습니다. 위의 기술을 사용하여 다음과 같은 사용자 정의 디버거 플러그인을 만들 수 있습니다

  • pytest_testrun_call()후크를 사용하여 AssertionError예외 를 잡습니다.
  • 역 추적에서 행 ‘해제’행 번호를 추출하고 일부 소스 코드 분석을 통해 성공적인 점프를 실행하는 데 필요한 어설 션 전후 행 번호를 결정합니다.
  • 테스트를 다시 실행 하지만 이번에 Pdb는 어설 션 이전 줄에 중단 점을 설정하고 중단 점에 도달하면 2 초로 자동으로 점프 한 다음 c계속을 수행하는 서브 클래스를 사용합니다 .

또는 어설 션이 실패하기를 기다리는 대신 assert테스트에서 찾은 각각에 대해 중단 점 설정을 자동화 할 수 있습니다 (소스 코드 분석을 사용하여 테스트 ast.Assert의 AST에있는 노드의 줄 번호를 간단하게 추출 할 수 있음 ). 디버거 스크립트 명령을 사용하고 jump명령을 사용 하여 어설 션 자체를 건너 뜁니다. 트레이드 오프를해야합니다. 디버거에서 모든 테스트를 실행하거나 (인터프리터가 모든 명령문에 대해 추적 함수를 호출해야하므로 느리게)이를 실패한 테스트에만 적용하고 해당 테스트를 처음부터 다시 실행하는 비용을 지불합니다.

이러한 플러그인은 많은 작업을 만들 것입니다. 여기서 예제에 쓰지 않을 것입니다. 일부 답변에 맞지 않기 때문에 부분적으로 가치가 있다고 생각하지 않기 때문 입니다. 디버거를 열고 수동으로 점프합니다. 어설 션이 실패하면 테스트 자체 또는 테스트중인 코드에 버그가 있음을 나타내므로 문제 디버깅에만 집중할 수 있습니다.


답변

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)

테스트가 실패하자마자 내장 파이썬 디버거로 테스트 할 수 있습니다. 디버깅이 완료 continue되면 나머지 테스트를 수행 할 수 있습니다 .


답변

PyCharm을 사용하는 경우 어설 션이 실패 할 때마다 예외 중단 점을 추가하여 실행을 일시 중지 할 수 있습니다. 중단 점보기 (CTRL-SHIFT-F8)를 선택하고 AssertionError에 대해 발생시 예외 처리기를 추가하십시오. 이로 인해 테스트 실행 속도가 느려질 수 있습니다.

그렇지 않으면 어설 션이 실패하는 시점이 아니라 각 실패한 테스트 가 끝날 때 (오류 직전)에 일시 중지 하지 않아도되는 경우 몇 가지 옵션이 있습니다. 그러나이 시점에서 테스트에서 연 파일 닫기와 같은 다양한 정리 코드가 이미 실행되었을 수 있습니다. 가능한 옵션은 다음과 같습니다.

  1. –pdb 옵션을 사용하여 pytest에게 오류가 발생했을 때 디버거에 빠지게 할 수 있습니다 .

  2. 다음 데코레이터를 정의하고 관련 테스트 기능을 각각 장식 할 수 있습니다. (메시지를 로깅하는 것 외에도이 시점에서 pdb.post_mortem 을 시작 하거나 대화 형 코드를 사용 하여이 답변에 설명 된 것처럼 예외가 발생한 프레임의 로컬과 상호 작용할 수도 있습니다.)

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. 모든 테스트 함수를 수동으로 꾸미지 않으려면 대신 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)


답변

Visual Studio Code를 기꺼이 사용하려는 경우 조건부 중단 점 을 사용하는 간단한 솔루션이 있습니다 .

예를 들어 어설 션을 설정할 수 있습니다.

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

그런 다음 어설 션이 실패 할 때만 중단되는 조건부 중단 점을 어설 션 줄에 추가하십시오.

여기에 이미지 설명을 입력하십시오


답변