pytest를 사용하여 오류가 발생하지 않았는지 확인하는 방법


83

다음과 같은 smth가 있다고 가정 해 봅시다.

import py, pytest

ERROR1 = ' --- Error : value < 5! ---'
ERROR2 = ' --- Error : value > 10! ---'

class MyError(Exception):
    def __init__(self, m):
        self.m = m

    def __str__(self):
        return self.m

def foo(i):
    if i < 5:
        raise MyError(ERROR1)
    elif i > 10:
        raise MyError(ERROR2)
    return i


# ---------------------- TESTS -------------------------
def test_foo1():
    with pytest.raises(MyError) as e:
        foo(3)
    assert ERROR1 in str(e)

def test_foo2():
    with pytest.raises(MyError) as e:
        foo(11)
    assert ERROR2 in str(e)

def test_foo3():
        ....
        foo(7)
         ....

Q : MyError가 발생하지 않는지 테스트하기 위해 test_foo3 ()을 어떻게 만들 수 있습니까? 내가 테스트 할 수 있다는 것은 분명합니다.

def test_foo3():
    assert foo(7) == 7

하지만 pytest.raises ()를 통해 테스트하고 싶습니다. 어쨌든 가능할까요? 예를 들어, "foo"함수에 반환 값이 전혀없는 경우

def foo(i):
    if i < 5:
        raise MyError(ERROR1)
    elif i > 10:
        raise MyError(ERROR2)

이 방법으로 테스트하는 것이 합리적 일 수 있습니다, imho.


문제를 찾는 것처럼 보이지만 코드 테스트 foo(7)는 괜찮습니다. 올바른 메시지가 표시되고 모든 pytest 출력으로 디버그하기가 더 쉽습니다. @Faruk ( 'Unexpected error...') 에서 강요 한 제안 은 오류에 대해 아무것도 말하지 않으며 멈출 것입니다. 더 나아질 수있는 유일한 방법은 의도를 다음과 같이 말하는 것 test_foo3_works_on_integers_within_range()입니다.
dhill

답변:


125

예기치 않은 예외가 발생하면 테스트가 실패합니다. foo (7)을 호출하면 MyError가 발생하지 않는지 테스트 할 수 있습니다. 따라서 다음으로 충분합니다.

def test_foo3():
    foo(7)

명시적이고 이에 대한 assert 문을 작성하려면 다음을 수행 할 수 있습니다.

def test_foo3():
    try:
        foo(7)
    except MyError:
        pytest.fail("Unexpected MyError ..")

3
고마워, 작동하지만 깨끗한 솔루션보다 해킹에 가깝습니다. 예를 들어, foo (4)에 대한 테스트는 실패하지만 어설 션 오류로 인해 실패하지 않습니다.
paraklet 2013

foo (4)에 대한 테스트는 예상하지 못한 예외가 발생하므로 실패합니다. 한 가지 다른 방법은 try catch 블록으로 래핑하고 특정 메시지로 실패하는 것입니다. 내 대답을 업데이트하겠습니다.
Faruk Sahin 2013

1
이와 같은 경우가 많으면 간단한 함수로 작성하는 것이 유용 할 수 있습니다.```def not_raises (error_class, func, * args, ** kwargs) : ...```또는 다음과 같이 작성할 수 있습니다. pytest와 같은 접근 방식. 그렇게한다면 모두에게 이익이되도록 PR을 작성하는 것이 좋습니다. :) (저장소는 bitbucket에 있습니다 ).
Bruno Oliveira

6
@paraklet-pytest의 주요 태그 라인은 "no-boilerplate testing" 입니다. pytest가 세부 사항을 처리하는 동안 Faruk의 첫 번째 예제에서와 같이 테스트를 작성할 수있게하는 것은 pytest의 정신입니다. 나에게 첫 번째 예는 "깨끗한 솔루션"이고 두 번째 예는 불필요하게 장황 해 보입니다.
Nick Chammas 2015

21

Oisin이 언급 한 것 위에 구축 ..

not_raisespytest와 유사 하게 작동 하는 간단한 함수를 만들 수 있습니다 raises.

from contextlib import contextmanager

@contextmanager
def not_raises(exception):
  try:
    yield
  except exception:
    raise pytest.fail("DID RAISE {0}".format(exception))

raises상대방을 유지하고 테스트를 더 읽기 쉽게 유지 하려는 경우 괜찮습니다 . 그러나 본질적으로 테스트하려는 코드 블록을 자체 줄에서 실행하는 것 외에는 실제로 필요하지 않습니다. pytest는 해당 블록이 오류를 발생시키는 즉시 실패합니다.


1
이것이 py.test에 빌드 되었으면합니다. 어떤 경우에는 테스트를 훨씬 더 읽기 쉽게 만들 것입니다. 특히 @pytest.mark.parametrize.
Arel

이 접근 방식의 코드 가독성에 매우 감사합니다!
GrazingScientist

6

not_raises가 작동하는지 궁금했습니다. 이것에 대한 빠른 테스트는 (test_notraises.py)입니다 :

from contextlib import contextmanager

@contextmanager
def not_raises(ExpectedException):
    try:
        yield

    except ExpectedException, err:
        raise AssertionError(
            "Did raise exception {0} when it should not!".format(
                repr(ExpectedException)
            )
        )

    except Exception, err:
        raise AssertionError(
            "An unexpected exception {0} raised.".format(repr(err))
        )

def good_func():
    print "hello"


def bad_func():
    raise ValueError("BOOM!")


def ugly_func():
    raise IndexError("UNEXPECTED BOOM!")


def test_ok():
    with not_raises(ValueError):
        good_func()


def test_bad():
    with not_raises(ValueError):
        bad_func()


def test_ugly():
    with not_raises(ValueError):
        ugly_func()

작동하는 것 같습니다. 그러나 나는 그것이 실제로 테스트에서 잘 읽는지 확실하지 않습니다.


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