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がトレースバックを印刷する方法で、whatever関数のどこで例外が発生したかを確認する方法は?


トレースバック全体、Ubuntu 14.04、Python 2.7.6を取得します
thefourtheye

@thefourtheye出力で要旨を確認してください。私はPython 2.7.4とUbunthu 14.04を試しました-メインポストで説明したのと同じ結果です。
ギルベイツ

1
@ギルベイツはあなたが正しい答えをマークできますか?
ゴンサロガルシア

回答:


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 2018年

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 2017

assert excinfo.match(r"^some info$")動作します
Robin Nemeth '15年

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")
Ilya Rusin

58

pytestでこのようなケースを処理するには2つの方法があります。

  • 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は常に進化しており、最近の素晴らしい変更の1つにより、同時にテストすることが可能になりました

  • 例外タイプ(厳密なテスト)
  • エラーメッセージ(正規表現を使用した厳密または緩いチェック)

ドキュメントからの2つの例:

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

4

このソリューションは私たちが使用しているものです:

def test_date_invalidformat():
    """
    Test if input incorrect data will raises ValueError exception
    """
    date = "06/21/2018 00:00:00"
    with pytest.raises(ValueError):
        app.func(date) #my function to be tested

pytestを参照してください、https: //docs.pytest.org/en/latest/reference.html#pytest-raises


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構文を使用することを「ベタープラクティス」とは呼びません。
ジャン=フランソワ・コルベット

2
「良い」とは言えないかもしれませんが、これは便利な代替手段です。答えの基準は実用性なので、賛成です。
Reb.Cabin

のように見えますpytestがより人気がnosexありますが、これは私がpytestを使用する方法です。
ギャング

0

「pytrace = True」を削除しようとしましたか?

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

'--fulltrace'で実行しようとしましたか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.