関数が予期した例外をスローしない場合にのみ失敗するユニットテストをどのように記述しますか?
関数が予期した例外をスローしない場合にのみ失敗するユニットテストをどのように記述しますか?
回答:
unittestモジュールからTestCase.assertRaises
(またはTestCase.failUnlessRaises
)を使用します。次に例を示します。
import mymod
class MyTestCase(unittest.TestCase):
def test1(self):
self.assertRaises(SomeCoolException, mymod.myfunc)
myfunc
には、それらをassertRaises呼び出しの引数として追加する必要があることに注意してください。ダリル・スピッツァーの答えを見てください。
self.assertRaises(TypeError, mymod.myfunc)
。組み込みの例外の完全なリストは、次の場所にあり
self.assertRaises(SomeCoolException, Constructor, arg1)
Python 2.7以降では、コンテキストマネージャを使用して、スローされた実際のExceptionオブジェクトを取得できます。
import unittest
def broken_function():
raise Exception('This is broken')
class MyTestCase(unittest.TestCase):
def test(self):
with self.assertRaises(Exception) as context:
broken_function()
self.assertTrue('This is broken' in context.exception)
if __name__ == '__main__':
unittest.main()
http://docs.python.org/dev/library/unittest.html#unittest.TestCase.assertRaises
Python 3.5、あなたはラップする必要がcontext.exception
でstr
そう、あなたが買ってあげます、TypeError
self.assertTrue('This is broken' in str(context.exception))
context.exception
メッセージを出しません。タイプです。
import unittest2
する場合はstr()
、を使用する必要がありますself.assertTrue('This is broken' in str(context.exception))
。
前の回答のコードは、次のように簡略化できます。
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction)
そして、関数が引数を取る場合は、次のようにそれらをassertRaisesに渡します。
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction, arg1, arg2)
2.7.15
ます。afunction
in self.assertRaises(ExpectedException, afunction, arg1, arg2)
がクラス初期化子の場合self
、最初の引数として渡す必要があります。たとえば、 self.assertRaises(ExpectedException, Class, self, arg1, arg2)
Python関数が例外をスローすることをどのようにテストしますか?
関数が予期される例外をスローしない場合にのみ失敗するテストを作成するにはどうすればよいですか?
self.assertRaises
メソッドをコンテキストマネージャとして使用します。
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
ベストプラクティスアプローチは、Pythonシェルでかなり簡単に示すことができます。
unittest
ライブラリー
Python 2.7または3の場合:
import unittest
Python 2.6では、unittest2unittest
と呼ばれる2.7のライブラリのバックポートをインストールし、それを次のようにエイリアスすることができます。unittest
import unittest2 as unittest
次に、Pythonシェルに次のPythonの型安全性のテストを貼り付けます。
class MyTestCase(unittest.TestCase):
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
def test_2_cannot_add_int_and_str(self):
import operator
self.assertRaises(TypeError, operator.add, 1, '1')
1つの用途をテストする assertRaises
コンテキストマネージャーとしてします。これにより、記録中にエラーが適切にキャッチおよびクリーンアップされることが保証されます。
なしで書くこともできますコンテキストマネージャます。テスト2を参照してください。最初の引数は、発生が予想されるエラーの種類、2番目の引数はテストする関数、残りの引数とキーワード引数はその関数に渡されます。
コンテキストマネージャを使用するだけの方が、はるかにシンプルで読みやすく、保守しやすいと思います。
テストを実行するには:
unittest.main(exit=False)
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
そしてあなたの端末は以下を出力するはずです:
..
----------------------------------------------------------------------
Ran 2 tests in 0.007s
OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>
そして、予想どおり、a 1
と'1'
結果をに追加しようとしていることがわかりTypeError
ます。
より詳細な出力については、これを試してください:
unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
コードは次のパターンに従う必要があります(これはユニットテストモジュールスタイルのテストです)。
def test_afunction_throws_exception(self):
try:
afunction()
except ExpectedException:
pass
except Exception:
self.fail('unexpected exception raised')
else:
self.fail('ExpectedException not raised')
Python <2.7では、この構成は予想される例外の特定の値をチェックするのに役立ちます。unittest関数assertRaises
は、例外が発生したかどうかのみをチェックします。
assertRaises
、FAILではなくERRORを取得することです。
から:http : //www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/
まず、これはファイルdum_function.pyの対応する(まだdum:p)関数です。
def square_value(a):
"""
Returns the square value of a.
"""
try:
out = a*a
except TypeError:
raise TypeError("Input should be a string:")
return out
実行するテストは次のとおりです(このテストのみが挿入されます)。
import dum_function as df # import function module
import unittest
class Test(unittest.TestCase):
"""
The class inherits from unittest
"""
def setUp(self):
"""
This method is called before each test
"""
self.false_int = "A"
def tearDown(self):
"""
This method is called after each test
"""
pass
#---
## TESTS
def test_square_value(self):
# assertRaises(excClass, callableObj) prototype
self.assertRaises(TypeError, df.square_value(self.false_int))
if __name__ == "__main__":
unittest.main()
これで機能をテストする準備ができました!テストを実行しようとすると、次のようになります。
======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_dum_function.py", line 22, in test_square_value
self.assertRaises(TypeError, df.square_value(self.false_int))
File "/home/jlengrand/Desktop/function.py", line 8, in square_value
raise TypeError("Input should be a string:")
TypeError: Input should be a string:
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
TypeErrorは、actullayが発生し、テストエラーを生成します。問題は、これがまさに私たちが望んだ動作であるということです。
このエラーを回避するには、テスト呼び出しでラムダを使用して関数を実行します。
self.assertRaises(TypeError, lambda: df.square_value(self.false_int))
最終的な出力:
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
パーフェクト!
...そして私にとっても完璧です!!
たくさんありがとうジュリアン・レングラント・ランバート氏
このテストアサートは実際には誤検知を返します。これは、「assertRaises」内のラムダが、テストされた関数ではなく型エラーを発生させる単位であるために発生します。
self.assertRaises(TypeError, df.square_value(self.false_int))
はメソッドを呼び出し、結果を返します。何が欲しいの方法および任意の引数を渡すとunittestのは、それを呼び出すことができるようにすることです:self.assertRaises(TypeError, df.square_value, self.false_int)
独自contextmanager
に作成して、例外が発生したかどうかを確認できます。
import contextlib
@contextlib.contextmanager
def raises(exception):
try:
yield
except exception as e:
assert True
else:
assert False
そして、あなたはraises
このように使うことができます:
with raises(Exception):
print "Hola" # Calls assert False
with raises(Exception):
raise Exception # Calls assert True
を使用している場合pytest
、この機能はすでに実装されています。あなたはできるpytest.raises(Exception)
:
例:
def test_div_zero():
with pytest.raises(ZeroDivisionError):
1/0
そしてその結果:
pigueiras@pigueiras$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items
tests/test_div_zero.py:6: test_div_zero PASSED
unittest
モジュールを必要としない回答を投稿していただきありがとうございます。
私はほとんどすべての場所でdoctest [1] を使用しています。これは、関数のドキュメント化とテストを同時に行うことが好きだからです。
このコードを見てください:
def throw_up(something, gowrong=False):
"""
>>> throw_up('Fish n Chips')
Traceback (most recent call last):
...
Exception: Fish n Chips
>>> throw_up('Fish n Chips', gowrong=True)
'I feel fine!'
"""
if gowrong:
return "I feel fine!"
raise Exception(something)
if __name__ == '__main__':
import doctest
doctest.testmod()
この例をモジュールに入れてコマンドラインから実行すると、両方のテストケースが評価およびチェックされます。
MockライブラリーがassertRaisesWithMessage()メソッドを(そのunittest.TestCaseサブクラスで)提供していることを発見しました。これは、予期される例外が発生するだけでなく、予期されるメッセージで発生することもチェックします。
from testcase import TestCase
import mymod
class MyTestCase(TestCase):
def test1(self):
self.assertRaisesWithMessage(SomeCoolException,
'expected message',
mymod.myfunc)
ここにはたくさんの答えがあります。このコードは、例外を作成する方法、メソッドでその例外を使用する方法、最後にユニットテストで正しい例外が発生することを確認する方法を示しています。
import unittest
class DeviceException(Exception):
def __init__(self, msg, code):
self.msg = msg
self.code = code
def __str__(self):
return repr("Error {}: {}".format(self.code, self.msg))
class MyDevice(object):
def __init__(self):
self.name = 'DefaultName'
def setParameter(self, param, value):
if isinstance(value, str):
setattr(self, param , value)
else:
raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)
def getParameter(self, param):
return getattr(self, param)
class TestMyDevice(unittest.TestCase):
def setUp(self):
self.dev1 = MyDevice()
def tearDown(self):
del self.dev1
def test_name(self):
""" Test for valid input for name parameter """
self.dev1.setParameter('name', 'MyDevice')
name = self.dev1.getParameter('name')
self.assertEqual(name, 'MyDevice')
def test_invalid_name(self):
""" Test to check if error is raised if invalid type of input is provided """
self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)
def test_exception_message(self):
""" Test to check if correct exception message and code is raised when incorrect value is passed """
with self.assertRaises(DeviceException) as cm:
self.dev1.setParameter('name', 1234)
self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')
if __name__ == '__main__':
unittest.main()
unittestモジュールからassertRaisesを使用できます
import unittest
class TestClass():
def raises_exception(self):
raise Exception("test")
class MyTestCase(unittest.TestCase):
def test_if_method_raises_correct_exception(self):
test_class = TestClass()
# note that you dont use () when passing the method to assertRaises
self.assertRaises(Exception, test_class.raises_exception)
すべての答えは完璧ですが、ユニットテストフレームワークに依存せず、テストクラスを記述する必要なく、関数が例外を発生させるかどうかをテストする方法を探していました。
私は次のように書いてしまいました:
def assert_error(e, x):
try:
e(x)
except:
return
raise AssertionError()
def failing_function(x):
raise ValueError()
def dummy_function(x):
return x
if __name__=="__main__":
assert_error(failing_function, 0)
assert_error(dummy_function, 0)
そしてそれは正しい行で失敗します:
Traceback (most recent call last):
File "assert_error.py", line 16, in <module>
assert_error(dummy_function, 0)
File "assert_error.py", line 6, in assert_error
raise AssertionError()
AssertionError