Python assert 例外

Neema Muganga 2023年10月10日
  1. コンテキストマネージャで Python の assert 例外をキャッチする
  2. キーワード引数を使用して Python アサート例外をキャッチする
Python assert 例外

この記事では、テストの単位としての assert を理解し​​、関数が必ずしも実行を終了せずに例外(コード実行中に検出されたエラー)をスローできることをテストします。つまり、スローされた例外はカプセル化されます。

このテストは、例外が発生した場合に合格します。予期されたもの以外の別の例外がスローされた場合、エラーがスローされます。発生した例外がまったくない場合、テストは失敗します。

コンテキストマネージャで Python の assert 例外をキャッチする

一般的な Python の概念で必然的に必要な場合にリソースの割り当てと解放を許可する方法と同様に、ここでのコンテキストは、テスト中にスローされる実際の例外オブジェクトを取得します。

発生した例外に対して追加のパフォーマンスチェックが必要な場合は、この例外属性をオブジェクトに格納します。

関数が失敗するか、例外がスローされるかどうかをテストするために、unittest モジュールの TestCase.assertRaises を使用します。

コンテキストマネージャを使用した実際の例を見てみましょう。

import unittest


class TestCase(unittest.TestCase):
    def test_nameerror(self):
        with self.assertRaises(Exception):
            100 * (someNumber / 5)


if __name__ == "__main__":
    unittest.main()

出力:

.
----------------------------------------------------------------------
Ran 1 test in 0.002s

OK

サンプルコードでは、例外がスローされることが予想されるため、テストに合格しています。5 を未定義の変数 someNumber で除算しています。したがって、この関数は NameError 例外をスローします。したがって、出力の最初の行に . が表示されているように、テストはパスしています。

例外がスローされないときにテストが失敗する例を見てみましょう。

import unittest


class TestCase(unittest.TestCase):
    def test_nameerror(self):
        with self.assertRaises(Exception):
            someNumber = 10
            100 * (someNumber / 5)


if __name__ == "__main__":
    unittest.main()

出力:

F
======================================================================
FAIL: test_nameerror (__main__.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):  File "c:\Users\Neema\Desktop\Article Requirement Spec and Example 
Articles\Article Requirement Spec 
and Example Articles\fah.py", line 106, in test_nameerror
    100 * (someNumber/5)
AssertionError: Exception not raised

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)

このサンプルコードでは、someNumber の値を定義し、それを使用して算術式を実行します。

出力は、テストが期待どおりに失敗したことを示しています。someNumber の定義済みの値が存在するため、今回は NameError 例外がスローされることはありません。

次の例のように、ユーザー定義の例外を使用してコンテキストマネージャーを実装することもできます。

import unittest


def user_function():
    raise Exception("A user-defined exception")


class MyTestCase(unittest.TestCase):
    def test_userexception(self):
        with self.assertRaises(Exception) as context:
            user_function()

        self.assertTrue("A user-defined exception" in str(context.exception))


if __name__ == "__main__":
    unittest.main()

出力:

.
----------------------------------------------------------------------
Ran 1 test in 0.006s

OK

TypeError が発生しないように、str を使用して context.exception を囲むことを忘れないでください。

ユニットテストでテスト値を true と比較する unittest ライブラリである assertTrue を紹介します。

assertTrue の代わりに assertIn を使用することもできます。

例:

import unittest


def user_function():
    raise Exception("A use defined exception")


class MyTestCase(unittest.TestCase):
    def test_userexception(self):
        with self.assertRaises(Exception) as context:
            user_function()

        self.assertIn("A use defined exception", str(context.exception))


if __name__ == "__main__":
    unittest.main()

テストに合格した場合と同じ出力が生成されます。

キーワード引数を使用して Python アサート例外をキャッチする

assertRaises() に例外を渡すだけのコンテキストマネージャーとは異なり、例外を呼び出すためのキーワード引数として関数呼び出しと関数のパラメーターも渡します。

構文

assertRaises(exception, function, *args, **keywords)

例:

import unittest


class MyTestCase(unittest.TestCase):
    def test_division_by_error(self):
        import operator

        self.assertRaises(ZeroDivisionError, operator.floordiv, 55, 0)


if __name__ == "__main__":
    unittest.main()

出力:

.
----------------------------------------------------------------------
Ran 1 test in 0.002s

OK

上記のサンプルコードでは、キーワード引数とともに assertRaises() を使用しました。数値をゼロで除算しようとした後に予期される ZeroDivisionError 例外を渡しました。floordiv 演算子関数で使用される演算子関数を 2 番目の引数としてインポートしました。ここでのパラメータ値は 3 番目のパラメータである 55 を 0 で割るためのものです。

assert の最後のアプリケーションは、assertRaises() に予期されたもの以外の別の例外を渡すときです。代わりにエラーが生成されます。

上で使用したのと同じサンプルコードを使用して、これを実装しましょう。

import unittest


class MyTestCase(unittest.TestCase):
    def test_division_by_error(self):
        import operator

        self.assertRaises(TypeError, operator.floordiv, 55, 0)


if __name__ == "__main__":
    unittest.main()

出力:

E
======================================================================
ERROR: test_division_by_error (__main__.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):  File "c:\Users\Neema\Desktop\Article Requirement Spec and Example 
Articles\user.py", line 16, in test_division_by_error
    self.assertRaises(TypeError, operator.floordiv, 55, 0)
  File "C:\Users\Neema\AppData\Local\Programs\Python\Python39\lib\unittest\case.py", line 733, in assertRaises
    return context.handle('assertRaises', args, kwargs)
  File "C:\Users\Neema\AppData\Local\Programs\Python\Python39\lib\unittest\case.py", line 201, in handle
    callable_obj(*args, **kwargs) 
ZeroDivisionError: integer division or modulo by zero

----------------------------------------------------------------------
Ran 1 test in 0.031s

FAILED (errors=1)

出力から、エラートレースバックが生成されます。数値をゼロで割ると、ZeroDivisionError 例外が発生することが予想されます。代わりに、文字列と整数など、異なるデータ型間で算術演算を実行するときに適用される TypeError 例外を渡します。

関連記事 - Python Exception