Pytestで例外が発生するかどうかをチェックする方法



TLドクター
時間は貴重な資源であるので、私はあなたを浪費しません.ここでは、どのように例外を発生させると主張することができますどのようにpytest .
使用方法 pytest.raises
import pytest

def test_raises_exception():
    with pytest.raises(ZeroDivisionError):
        1 / 0
そして、どのようにあなたがアサートするかは例外です.
解決法:あなたのコードをtry/except ブロックし、コードが発生する場合は、それをキャッチし、素敵なメッセージを印刷することができます.pytest あなたはそれをキャッチしない場合でも、テストを失敗させるに十分なスマートですが、メッセージをあなたのテストクリーナーになります.
def my_division_function(a, b):
    return a / b

def test_code_raises_no_exception():
    """
    Assert your python code raises no exception.    
    """
    try:
        my_division_function(10, 5)
    except ZeroDivisionError as exc:
        assert False, f"'10 / 5' raised an exception {exc}"
そして、それは、あなたがもっと知りたい場合は、沿って従ってください.

導入
このチュートリアルでは、使用方法を学びますpytest です.
  • 例外が発生すると主張する
  • 例外メッセージをアサートする
  • 例外型をアサートする
  • 例外が発生しないことを表明する
  • 一言で言えば、我々はどのように使用するかを見るでしょうpytest.raises これらの例の例を挙げてください.

    目次
  • How to Assert That an Exception Is Raised
  • How to Assert That NO Exception Is Raised
  • How to Assert the Exception Message - And Type
  • Conclusion

  • 例外が発生すると主張する方法
    この節では、コードが例外を発生させるという主張をどのようにするかを示します.これは頻繁に使用するケースであり、時々トリッキーです.あなたが使っているなら、素晴らしいことはpytest あなたは慣用的でクリーンな方法でそれを行うことができます.
    辞書のキーをチェックする関数があることを想像しましょう.キーが存在しない場合はKeyError . ご覧のように、これは非常に一般的であり、ユーザーにエラーについて多くのことを伝えません.我々は、フィールドに応じて別のメッセージを使用して、カスタムの例外を高めることによってそれをきれいにすることができます.
    import pytest
    
    
    class MissingCoordException(Exception):
        """Exception raised when X or Y is not present in the data."""
    
    
    class MissingBothCoordException(Exception):
        """Exception raised when both X and Y are not present in the data."""
    
    
    def sum_x_y(data: dict) -> str:
        return data["x"] + data["y"]
    
    さて、これをテストする時間.どうすればいいのかpytest ?
    このコードは意図的に間違っています.実際には、TDDのように最初にテスト失敗を見たい.テスト失敗を見た後、我々の実装を修正し、テストを再実行できます.
    def test_sum_x_y_missing_both():
        data = {"irrelevant": 1}
        with pytest.raises(MissingBothCoordException):
            sum_x_y(data)
    
    次に、次の出力を取得します.
    ============================ FAILURES ============================
    ________________ test_sum_x_y_missing_both _________________
    
        def test_sum_x_y_missing_both():
            data = {"irrelevant": 1}
            with pytest.raises(MissingBothCoordException):
    >           sum_x_y(data)
    
    test_example.py:33: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    data = {'irrelevant': 1}
    
        def sum_x_y(data: dict) -> str:
    >       return data["x"] + data["y"]
    E       KeyError: 'x'
    
    test_example.py:27: KeyError
    ==================== short test summary info =====================
    FAILED test_example.py::test_sum_x_y_missing_both - KeyEr...
    ======================= 1 failed in 0.02s ========================
    
    OK、これは意味をなす、今それを修正する時間です.データをチェックするdict 両方ともx and y , そうでなければ我々はMissingBothCoordException .
    def sum_x_y(data: dict) -> str:
        if "x" not in data and "y" not in data:
            raise MissingBothCoordException("Both x and y coord missing.")
        return data["x"] + data["y"]
    
    そして、我々がテストを再実行するとき、それは通ります.
    test_example.py .                                          [100%]
    
    ======================= 1 passed in 0.01s ========================
    
    すごい!そして、それはかなりそれです.これは例外が発生したかどうかをチェックする方法ですpytest . 次のセクションでは、我々は我々の機能を改善するつもりです、そして、我々はもう一つのテストを必要とします.

    例外メッセージとタイプをアサートする方法
    このセクションでは、我々は改善されますsum_x_y 関数とテスト.例外メッセージをチェックすることでテストをより堅牢にする方法を示します.
    それを念頭に置きましょうsum_x_y 関数.
    def sum_x_y(data: dict) -> str:
        if "x" not in data and "y" not in data and "extra" not in data:
            raise MissingBothCoordException("Both X and Y coord missing.")
        if "x" not in data:
            raise MissingCoordException("The Y coordinate is not present in the data.")
        if "y" not in data:
            raise MissingCoordException("The Y coordinate is not present in the data.")
        return data["x"] + data["y"]
    
    新しいテストは次のようになります.
    def test_sum_x_y_has_x_missing_coord():
        data = {"extra": 1, "y": 2}
        with pytest.raises(MissingCoordException):
            sum_x_y(data)
    
    そして、それは通過します!
    $ poetry run pytest -k test_sum_x_y_has_x_missing_coord
    ====================== test session starts =======================
    collected 2 items / 1 deselected / 1 selected                    
    
    test_example.py .                                          [100%]
    
    ================ 1 passed, 1 deselected in 0.01s =================
    
    しかし、それは少し壊れやすいです.あなたがそれに気づいていなかった場合は"x" が見つからない場合、例外メッセージは次のようになります."The Y coordinate is not present in the data." . これはバグです、そして、それを見つける1つの方法は我々が正しいメッセージを返すことによってあります.ありがたいことに.pytest 簡単に行うことができます.
    テストを再評価してメッセージを考慮すると、次の出力が得られます.
    def test_sum_x_y_has_x_missing_coord():
        data = {"extra": 1, "y": 2}
        with pytest.raises(MissingCoordException) as exc:
            sum_x_y(data)
        assert "The X coordinate is not present in the data." in str(exc.value)
    
    ============================ FAILURES ============================
    _____________ test_sum_x_y_has_x_missing_coord _____________
    
    def test_sum_x_y_has_x_missing_coord():
            data = {"extra": 1, "y": 2}
            with pytest.raises(MissingCoordException) as exc:
                sum_x_y(data)
    >       assert "The X coordinate is not present in the data." in str(exc.value)
    E       AssertionError: assert 'The X coordinate is not present in the data.' in 'The Y coordinate is not present in the data.'
    E        +  where 'The Y coordinate is not present in the data.' = str(MissingCoordException('The Y coordinate is not present in the data.'))
    E        +    where MissingCoordException('The Y coordinate is not present in the data.') = <ExceptionInfo MissingCoordException('The Y coordinate is not present in the data.') tblen=2>.value
    
    test_example.py:32: AssertionError
    ==================== short test summary info =====================
    FAILED test_example.py::test_sum_x_y_has_x_missing_coord
    ======================= 1 failed in 0.02s ========================
    
    それはまさに私たちが望むものです.コードを修正してテストを再実行しましょう.
    def sum_x_y(data: dict) -> str:
        if "x" not in data and "y" not in data and "extra" not in data:
            raise MissingBothCoordException("Both X and Y coord missing.")
        if "x" not in data:
            raise MissingCoordException("The X coordinate is not present in the data.")
        if "y" not in data:
            raise MissingCoordException("The Y coordinate is not present in the data.")
        return data["x"] + data["y"]
    
    結果.
    $ poetry run pytest test_example.py::test_sum_x_y_has_x_missing_coord
    ====================== test session starts =======================
    platform linux -- Python 3.8.5, pytest-6.1.1, py-1.9.0, pluggy-0.13.1
    rootdir: /home/miguel/projects/tutorials/pytest-raises
    collected 1 item                                                 
    
    test_example.py .                                          [100%]
    
    ======================= 1 passed in 0.01s ========================
    
    これは可能ですpytest.raises 返り値ExceptionInfo フィールドを含むオブジェクトtype , value , traceback and many others . 我々が主張したいならばtype , 我々は、これらの線に沿って何かをすることができました.
    def test_sum_x_y_has_x_missing_coord():
        data = {"extra": 1, "y": 2}
        with pytest.raises(MissingCoordException) as exc:
            sum_x_y(data)
        assert "The X coordinate is not present in the data." in str(exc.value)
        assert exc.type == MissingCoordException
    
    しかし、我々はすでに使用してpytest.raises それで、私はこのようなタイプを少し冗長であると主張すると思います.この時はいつ役に立つのですか.これは私たちがより一般的な例外をアサートしている場合に便利ですpytest.raises そして、正確な例外をチェックしたい.例えば:
    def test_sum_x_y_has_x_missing_coord():
        data = {"extra": 1, "y": 2}
        with pytest.raises(Exception) as exc:
            sum_x_y(data)
        assert "The X coordinate is not present in the data." in str(exc.value)
        assert exc.type == MissingCoordException
    
    メッセージをアサートするもう一つの方法はmatch 指定したいパターンの引数.以下の例は公式から取ったものですpytest docs .
    >>> 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")
    
    ご覧のように、期待される例外が発生した場合でも、メッセージがregexパターンに一致するかどうかを確認できます.

    例外が発生しないと主張する方法
    このチュートリアルの最後のセクションでは、もう一つの一般的な使用例について説明します.例外がスローされないことをアサートする方法.我々がそうすることができる1つの方法は、Atry / except . それが例外を上げるならば、我々はそれを捕えて、偽を主張します.
    def test_sum_x_y_works():
        data = {"extra": 1, "y": 2, "x": 1}
    
        try:
            sum_x_y(data)
        except Exception as exc:
            assert False, f"'sum_x_y' raised an exception {exc}"
    
    このテストを実行すると、パスします.
    $ poetry run pytest test_example.py::test_sum_x_y_works
    ====================== test session starts =======================
    collected 1 item                                                 
    
    test_example.py .                                          [100%]
    
    ======================= 1 passed in 0.00s ========================
    
    さて、テスト失敗を見ることができるように意図的なバグを作りましょう.我々は、我々の機能を変えるために、我々を変えますValueError 結果を返す前に.
    def sum_x_y(data: dict) -> str:
        if "x" not in data and "y" not in data and "extra" not in data:
            raise MissingBothCoordException("'extra field and x / y coord missing.")
        if "x" not in data:
            raise MissingCoordException("The X coordinate is not present in the data.")
        if "y" not in data:
            raise MissingCoordException("The Y coordinate is not present in the data.")
        raise ValueError("Oh no, this shouldn't have happened.")
        return data["x"] + data["y"]
    
    そして、テストを再実行します.
        def test_sum_x_y_works():
            data = {"extra": 1, "y": 2, "x": 1}
    
            try:
                sum_x_y(data)
            except Exception as exc:
    >           assert False, f"'sum_x_y' raised an exception {exc}"
    E           AssertionError: 'sum_x_y' raised an exception Oh no, this shouldn't have happened.
    E           assert False
    
    test_example.py:52: AssertionError
    ==================== short test summary info =====================
    FAILED test_example.py::test_sum_x_y_works - AssertionErr...
    ======================= 1 failed in 0.02s ========================
    
    動く!我々のコードはValueError そして、テストは失敗しました!

    結論
    それは今日、人々のためだ!あなたが何か新しいことを学んだことを願っています.例外をテストする方法を知ることは重要なスキルです.道pytest それは、Imhoよりきれいですunittest そして、より冗長な.この記事では、あなたのコードが期待される例外を発生させると主張するだけでなく、提起されるべきでないときにアサートする方法を示しました.最後に、私たちは、例外メッセージがあなたが予想するものであるかどうかチェックする方法を見ました.
    閉じるこの動画はお気に入りから削除されています
  • 3 Ways to Unit Test REST APIs in Python
  • Everything You Need to Know About Python's Namedtuples
  • The Best Way to Compare Two Dictionaries in Python
  • How to Choose Between isdigit(), isdecimal() and isnumeric() in Python
  • 7 pytest Features and Plugins That Will Save You Tons of Time
  • How to Check if an Exception Is Raised (or Not) With pytest 初登場miguendes's blog .
    また会いましょう!