pytest에서 예외가 발생했다고 올바르게 주장하는 방법은 무엇입니까?


292

암호:

# coding=utf-8
import pytest


def whatever():
    return 9/0

def test_whatever():
    try:
        whatever()
    except ZeroDivisionError as exc:
        pytest.fail(exc, pytrace=True)

산출:

================================ test session starts =================================
platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2
plugins: django, cov
collected 1 items 

pytest_test.py F

====================================== FAILURES ======================================
___________________________________ test_whatever ____________________________________

    def test_whatever():
        try:
            whatever()
        except ZeroDivisionError as exc:
>           pytest.fail(exc, pytrace=True)
E           Failed: integer division or modulo by zero

pytest_test.py:12: Failed
============================== 1 failed in 1.16 seconds ==============================

pytest print traceback을 만드는 방법, whatever함수에서 예외가 발생한 위치를 볼 수 있습니까?


나는 전체 역 추적, 우분투 14.04, 파이썬 2.7.6 얻을
thefourtheye

@thefourtheye 출력과 함께 요점을 확인하십시오. 필자는 Python 2.7.4 및 Ubunthu 14.04를 사용해 보았습니다. 메인 포스트에서 설명한 것과 동일한 결과를 얻었습니다.
Gill Bates

1
@ GilBates는 정답을 표시 할 수 있습니까 ??
Gonzalo Garcia

답변:


330

pytest.raises(Exception) 당신이 필요한 것입니다.

암호

import pytest

def test_passes():
    with pytest.raises(Exception) as e_info:
        x = 1 / 0

def test_passes_without_info():
    with pytest.raises(Exception):
        x = 1 / 0

def test_fails():
    with pytest.raises(Exception) as e_info:
        x = 1 / 1

def test_fails_without_info():
    with pytest.raises(Exception):
        x = 1 / 1

# Don't do this. Assertions are caught as exceptions.
def test_passes_but_should_not():
    try:
        x = 1 / 1
        assert False
    except Exception:
        assert True

# Even if the appropriate exception is caught, it is bad style,
# because the test result is less informative
# than it would be with pytest.raises(e)
# (it just says pass or fail.)

def test_passes_but_bad_style():
    try:
        x = 1 / 0
        assert False
    except ZeroDivisionError:
        assert True

def test_fails_but_bad_style():
    try:
        x = 1 / 1
        assert False
    except ZeroDivisionError:
        assert True

산출

============================================================================================= test session starts ==============================================================================================
platform linux2 -- Python 2.7.6 -- py-1.4.26 -- pytest-2.6.4
collected 7 items 

test.py ..FF..F

=================================================================================================== FAILURES ===================================================================================================
__________________________________________________________________________________________________ test_fails __________________________________________________________________________________________________

    def test_fails():
        with pytest.raises(Exception) as e_info:
>           x = 1 / 1
E           Failed: DID NOT RAISE

test.py:13: Failed
___________________________________________________________________________________________ test_fails_without_info ____________________________________________________________________________________________

    def test_fails_without_info():
        with pytest.raises(Exception):
>           x = 1 / 1
E           Failed: DID NOT RAISE

test.py:17: Failed
___________________________________________________________________________________________ test_fails_but_bad_style ___________________________________________________________________________________________

    def test_fails_but_bad_style():
        try:
            x = 1 / 1
>           assert False
E           assert False

test.py:43: AssertionError
====================================================================================== 3 failed, 4 passed in 0.02 seconds ======================================================================================

e_info예외 개체 를 저장하여 세부 정보를 추출 할 수 있습니다. 예를 들어 예외 호출 스택 또는 다른 중첩 예외를 확인하려는 경우.


34
실제로 쿼리하는 예제를 포함시킬 수 있다면 좋을 것이다 e_info. 다른 특정 언어에 익숙한 개발자의 경우 범위가 블록 e_info외부로 확장 되는 것은 분명하지 않습니다 with.
cjs

152

다음과 같은 의미입니까?

def test_raises():
    with pytest.raises(Exception) as execinfo:   
        raise Exception('some info')
    # these asserts are identical; you can use either one   
    assert execinfo.value.args[0] == 'some info'
    assert str(execinfo.value) == 'some info'

16
excinfo.value.message나를 위해 작동하지 않았다, 사용했다 str(excinfo.value), 새로운 답변을 추가
d_j

5
@d_j assert excinfo.value.args[0] == 'some info'는 메시지에 직접 액세스하는 방법입니다.
maxschlepzig

assert excinfo.match(r"^some info$")잘 작동
Robin Nemeth

14
버전 때문에 3.1당신은 키워드 인수를 사용할 수 있습니다 match: 예외 텍스트 또는 정규식과 일치하는 주장에 with raises(ValueError, match='must be 0 or None'): raise ValueError("value must be 0 or None")with raises(ValueError, match=r'must be \d+$'): raise ValueError("value must be 42")
일리아 Rusin

58

pytest에서 이러한 종류의 사례를 처리하는 두 가지 방법이 있습니다.

  • pytest.raises기능 사용

  • pytest.mark.xfail데코레이터 사용하기

사용법 pytest.raises:

def whatever():
    return 9/0
def test_whatever():
    with pytest.raises(ZeroDivisionError):
        whatever()

사용법 pytest.mark.xfail:

@pytest.mark.xfail(raises=ZeroDivisionError)
def test_whatever():
    whatever()

출력 pytest.raises:

============================= test session starts ============================
platform linux2 -- Python 2.7.10, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 -- 
/usr/local/python_2.7_10/bin/python
cachedir: .cache
rootdir: /home/user, inifile:
collected 1 item

test_fun.py::test_whatever PASSED


======================== 1 passed in 0.01 seconds =============================

pytest.xfail마커 출력 :

============================= test session starts ============================
platform linux2 -- Python 2.7.10, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 -- 
/usr/local/python_2.7_10/bin/python
cachedir: .cache
rootdir: /home/user, inifile:
collected 1 item

test_fun.py::test_whatever xfail

======================== 1 xfailed in 0.03 seconds=============================

현상태대로 설명서를 말한다 :

pytest.raises예외를 테스트하는 경우 의도적으로 자체 코드에서 의도적으로 발생하는 예외에 대해 사용 하는 것이 좋을 수도 있지만 @pytest.mark.xfail, 검사 기능과 함께 사용 하면 수정되지 않은 버그 (테스트에서 "어떻게 발생해야 하는지를 설명하는") 또는 종속성의 버그를 문서화하는 것과 같은 경우에 더 좋습니다. .


xfail여기서 문제에 대한 해결책이 아니며 테스트가 실패 할 수 있습니다. 여기에서 특정 예외가 발생했는지 확인하고 싶습니다.
Ctrl-C

44

당신은 시도 할 수 있습니다

def test_exception():
    with pytest.raises(Exception) as excinfo:   
        function_that_raises_exception()   
    assert str(excinfo.value) == 'some info' 

pytest 5.0.0에서 예외 메시지 / 값을 문자열로 가져 오려면을 str(excinfo.value)사용해야합니다. pytest 4.x에서도 작동합니다. pytest 4.x str(excinfo)에서도 작동하지만 pytest 5.0.0 에서는 작동 하지 않습니다 .
Makyen

이것이 내가 찾던 것입니다. 감사합니다
Eystein Bye

15

pytest는 끊임없이 발전하고 최근의 멋진 변화 중 하나를 통해 동시에 테스트 할 수 있습니다

  • 예외 유형 (엄격한 테스트)
  • 오류 메시지 (정규 표현식을 사용하여 엄격하거나 느슨하게 검사)

설명서의 두 가지 예 :

with pytest.raises(ValueError, match='must be 0 or None'):
    raise ValueError('value must be 0 or None')
with pytest.raises(ValueError, match=r'must be \d+$'):
    raise ValueError('value must be 42')

나는 많은 프로젝트에서 그 접근법을 사용하고 있으며 매우 좋아합니다.


6

올바른 방법을 사용하고 pytest.raises있지만 여기 주석에서 흥미로운 대안을 찾았 으며이 질문을 읽는 독자를 위해 저장하고 싶습니다.

try:
    thing_that_rasises_typeerror()
    assert False
except TypeError:
    assert True


2

더 나은 연습은 unittest.TestCase를 상속하는 클래스를 사용하고 self.assertRaises를 실행하는 것입니다.

예를 들면 다음과 같습니다.

import unittest


def whatever():
    return 9/0


class TestWhatEver(unittest.TestCase):

    def test_whatever():
        with self.assertRaises(ZeroDivisionError):
            whatever()

그런 다음 다음을 실행하여 실행합니다.

pytest -vs test_path

4
무엇보다 더 나은 연습? 나는 pytest 구문 "더 나은 연습"대신 unittest 구문을 사용하여 호출하지 않을 것입니다.
Jean-François Corbett

2
더 낫지는 않지만 유용한 대안입니다. 답변의 기준은 실용적이기 때문에 찬성합니다.
Reb. Cabin

pytest보다 인기가있는 것처럼 nosex보이지만 이것이 pytest를 사용하는 방법입니다.
Gang

0

"pytrace = True"를 제거하려고 했습니까?

pytest.fail(exc, pytrace=True) # before
pytest.fail(exc) # after

'--fulltrace'로 실행하려고 했습니까?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.