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)

この方法でテストするのは理にかなっています、私見。


問題を探しているようfoo(7)です。コードテストは問題ありません。適切なメッセージが表示され、すべてのpytest出力を使用してデバッグするのが簡単になります。@Faruk('Unexpected error...')から強制された提案は、エラーについて何も述べておらず、行き詰まります。それをより良くするためにあなたができる唯一のことはあなたの意図をのように述べることですtest_foo3_works_on_integers_within_range()
dhill 2013

回答:


125

予期しない例外が発生した場合、テストは失敗します。foo(7)を呼び出すだけで、MyErrorが発生しないことをテストできます。したがって、以下で十分です。

def test_foo3():
    foo(7)

明示的にして、このためのアサートステートメントを記述したい場合は、次のことができます。

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

3
おかげで、それは機能しますが、それはクリーンな解決策というよりはハックのようです。たとえば、foo(4)のテストは失敗しますが、アサーションエラーが原因ではありません。
パラクレートス2013年

foo(4)のテストは、予期しない例外をスローするため失敗します。もう1つの方法は、try catchブロックでラップし、特定のメッセージで失敗することです。答えを更新します。
Faruk Sahin 2013年

1
このようなケースがたくさんある場合は、単純な関数でそれを書くと便利かもしれません: `` `def not_raises(error_class、func、* args、** kwargs):...` ``またはあなたは書くことができますwith-pytestのようなアプローチ。もしそうなら、私はあなたがすべてに利益をもたらすためにこれでPRを書くことを提案します。:)(リポジトリはbitbucketにあります)。
ブルーノオリベイラ

6
@ paraklet-pytestの主なキャッチフレーズは「ボイラープレートなしのテスト」です。pytestが詳細を処理している間に、Farukの最初の例のようにテストを記述できるようにすることは、pytestの精神に非常に基づいています。私にとって、最初の例は「クリーンなソリューション」であり、2番目の例は不必要に冗長に見えます。
Nick Chammas 2015

21

オシーンが言ったことの上に構築する。

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
アレル2017

このアプローチでのコードの可読性の感覚に非常に感謝しています!
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.