Python モック レイズ例外
この記事の主な目的は、単体テスト ライブラリ unittest
を使用するときに例外をスローする方法を示すことです。
Python で単体テスト ライブラリ unittest
を使用するときに例外をスローする
コードが安定しており、プロジェクトのスコープ内のほとんどのユース ケースに対応していることを確認するには、さまざまな入力を与えてテストする必要があります。 Python に存在するライブラリの 1つである unittest
は、そのような機能を提供し、さまざまなテスト ケースを記述して実行できるようにします。
テスト中は、入力が予期しない場合を処理できる必要があります。 このようなケースは、指定された入力の何が問題なのかを詳しく説明する例外をスローすることで処理できます。
次のコードを検討してください。
class A:
def __init__(self):
pass
def computeData(self):
return 0
class B:
def method_to_test():
obj = A()
try:
print(obj.computeData())
except Exception as e:
print("Exception at method_to_test: " + str(e))
テストしたい関数が method_to_test
だとしましょう。 別のクラス A
のオブジェクトを初期化し、computeData
という名前のメソッドの 1つを try-except
ブロックで呼び出して例外をキャッチします。
特定の例外がスローされたときの computeData
の動作をテストしたい場合は、unittest
モジュールを使用して行う必要があります。
例外を 1 回発生させる
上記のシナリオを考えると、いつ例外をスローするかをより細かく制御したい場合もあります。
そのような必要性が生じる理由はたくさんあります。 コードの実装によっては、動作をより詳細に制御できると非常に役立ちます。
例外をアサート
テスト ケースを作成しているときに、例外がトリガーされるかどうかを確認したい場合があります。 これは、例外がスローされたかどうかを知りたい場合に特に便利です。
これは、何か問題が発生した場合に、テスト ケースの早い段階で見つけるのに役立ちます。
サードパーティ モジュールのモック
サードパーティのモジュールは、実装に応じて、ほとんどない場合から多数の場合までさまざまです。 サードパーティ製モジュールが意図したとおりに動作するかどうかをテストするには、さまざまな出力でその動作もテストする必要があります。
そのような例の 1つは request
です。これは、ネットワーク経由で通信したり、HTTP を使用してデータを送受信したりするときに広く使用されているモジュールです。
関数から手動で例外を発生させる
computeData
関数から手動で例外をスローするには、side_effect
属性を使用する必要があります。
次のコードを検討してください。
import unittest
from unittest.mock import patch
from unittest.mock import MagicMock
class A:
def __init__(self) -> None:
pass
def computeData(self):
return 0
class B:
def method_to_test():
obj = A()
try:
print(obj.computeData())
except Exception as e:
print("Exception at method_to_test: " + str(e))
class Test(unittest.TestCase):
@patch.object(A, "computeData", MagicMock(side_effect=Exception("Test")))
def test_method(self):
B.method_to_test()
if __name__ == "__main__":
Test().test_method()
出力:
Exception at method_to_test: Test
このソリューションでは、メソッド method_to_test
をテストするために、新しいメソッド test_method
が作成されます。 関数が patch.object
で装飾されていることに注意することも重要です。
patch
デコレーターは、モジュールとクラスレベルの属性にパッチを適用するためにモジュールで提供されます。 何にパッチを当てるかをより具体的にするために、patch
の代わりに patch.object
を使用してメソッドに直接パッチを当てます。
デコレーターでは、クラス名 A
が渡されます。 これは、パッチが適用されるオブジェクトが computeData
というメンバー名が渡された A
の一部であることを示します。
最後のパラメーターは、Mock
のサブクラスである MagicMock
オブジェクトです。この場合、side_effect
属性を使用して、必要な動作を computeData
に関連付けます。この場合、例外をスローします。
プログラムの大まかな流れは次のとおりです。
test_method
が呼び出されます。- クラス
A
のcomputeData
にパッチが適用されます。side_effect
が割り当てられていますが、この場合は例外です。 - クラス
B
のmethod_to_test
が呼び出されます。 - クラス
A
のコンストラクターが呼び出され、インスタンスがobj
に格納されます。 computeData
が呼び出されます。 メソッドにパッチが適用されているため、例外がスローされます。
例外を 1 回発生させる
例外を 1 回だけ発生させるか、関数の動作をより詳細に制御するために、side_effect
は単なる関数呼び出し以上のものをサポートします。
現在、side_effect
は以下をサポートしています:
反復可能
呼び出し可能
例外
(インスタンスまたはクラス)
Iterable
を使用すると、メソッドで例外を 1 回だけ発生させることができます。
class Test(unittest.TestCase):
@patch.object(A, "computeData", MagicMock(side_effect=[1, Exception("Test"), 3]))
def test_method(self):
B.method_to_test()
if __name__ == "__main__":
testClass = Test()
testClass.test_method()
testClass.test_method()
testClass.test_method()
出力:
1
Exception at method_to_test: Test
3
渡された Iterable
により、選択した要素にパッチが適用され、いつ例外を発生させるかを制御できるようになります。 したがって、関数が 2 回目に呼び出されたときにのみ例外がスローされ、それ以外の場合は 1
と 3
が返されます。
例外をアサート
特定の例外が発生したかどうかを判断するには、目的の例外がスローされない場合に unittest.TestCase.assertRaises
を使用できます。
class Test(unittest.TestCase):
@patch.object(A, "computeData", MagicMock(side_effect=Exception("Test")))
def test_method(self):
self.assertRaises(KeyError, A.computeData)
if __name__ == "__main__":
Test().test_method()
出力:
Exception at method_to_test: Test
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 28, in <module>
Test().test_method()
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
return func(*newargs, **newkeywargs)
File "d:\Python Articles\a.py", line 25, in test_method
self.assertRaises(KeyError, A.computeData)
File "C:\Program Files\Python310\lib\unittest\case.py", line 738, in assertRaises
return context.handle('assertRaises', args, kwargs)
File "C:\Program Files\Python310\lib\unittest\case.py", line 201, in handle
callable_obj(*args, **kwargs)
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1104, in __call__
return self._mock_call(*args, **kwargs)
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1108, in _mock_call
return self._execute_mock_call(*args, **kwargs)
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1163, in _execute_mock_call
raise effect
File "d:\Python Articles\a.py", line 17, in method_to_test
Exception: Test
test_method
メソッドに小さな変更を加えて、2つのパラメーターを指定して assertRaises
を呼び出すと、上記の出力が得られます。
2つのパラメーターは次のように定義できます。
KeyError
- トリガーしたいすべての例外がここに渡されます。A.computeData
- 例外をスローするメソッド。
サードパーティ モジュールのモック
前のように post
をモックするには、patch
デコレーターを使用する必要があります。 簡単な例は次のとおりです。
class Test(unittest.TestCase):
@patch(
"requests.post", MagicMock(side_effect=requests.exceptions.ConnectionError())
)
def test_method(self):
requests.post()
出力:
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 33, in <module>
Test().test_method()
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
return func(*newargs, **newkeywargs)
File "d:\Python Articles\a.py", line 30, in test_method
requests.post()
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1104, in __call__
return self._mock_call(*args, **kwargs)
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1108, in _mock_call
return self._execute_mock_call(*args, **kwargs)
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1163, in _execute_mock_call
raise effect
requests.exceptions.ConnectionError
patch
デコレータを使用して、サードパーティ モジュールの関数にパッチを適用することもできます。これにより、デバッグが改善され、製品/プログラムの信頼性の規模をより詳細に制御できるようになります。
Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.
LinkedIn関連記事 - Python Exception
- Python でのオープン ファイルの例外処理
- Python で例外を再スローする
- Python で例外を発生させる
- Python assert 例外
- Python で try ... else ブロックを使用する