Sadece bir işlev beklenen bir istisnayı atmazsa başarısız olan bir birim testi nasıl yazılır?
Sadece bir işlev beklenen bir istisnayı atmazsa başarısız olan bir birim testi nasıl yazılır?
Yanıtlar:
Unittest modülünden TestCase.assertRaises
(veya TestCase.failUnlessRaises
) kullanın , örneğin:
import mymod
class MyTestCase(unittest.TestCase):
def test1(self):
self.assertRaises(SomeCoolException, mymod.myfunc)
myfunc
Eğer assertRaises çağrı argümanlar olarak eklemeniz gerekir. Daryl Spitzer'in cevabına bakınız.
self.assertRaises(TypeError, mymod.myfunc)
. Yerleşik İstisnaların tam bir listesini burada bulabilirsiniz: docs.python.org/3/library/exceptions.html#bltin-exceptions
self.assertRaises(SomeCoolException, Constructor, arg1)
Python 2.7'den bu yana, atılan gerçek İstisna nesnesini elde etmek için bağlam yöneticisini kullanabilirsiniz:
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
In Python 3.5 , sen sarmak zorunda context.exception
içinde str
aksi takdirde bir alırsınız,TypeError
self.assertTrue('This is broken' in str(context.exception))
context.exception
mesajı vermez; bir tür.
import unittest2
, kullanmanız gerekir str()
, yani self.assertTrue('This is broken' in str(context.exception))
.
Önceki cevabımdaki kod basitleştirilebilir:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction)
Ve eğer işlev argüman alırsa, onları assert'e aktarın.
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction, arg1, arg2)
2.7.15
. Eğer afunction
içinde self.assertRaises(ExpectedException, afunction, arg1, arg2)
sınıf başlatıcısı, sen geçmesi gerekiyor self
ilk argüman ör gibi self.assertRaises(ExpectedException, Class, self, arg1, arg2)
Bir Python fonksiyonunun bir istisna verdiğini nasıl test edersiniz?
Nasıl bir işlev beklenen bir istisnayı atmazsa başarısız olan bir test nasıl yazılır?
self.assertRaises
Yöntemi bir bağlam yöneticisi olarak kullanın :
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
En iyi uygulama yaklaşımını bir Python kabuğunda göstermek oldukça kolaydır.
unittest
kütüphane
Python 2.7 veya 3'te:
import unittest
Python 2.6'da, unittest2 unittest
adlı 2.7'nin kütüphanesini ve sadece şu diğer adı içeren bir backport yükleyebilirsiniz unittest
:
import unittest2 as unittest
Şimdi, aşağıdaki Python'un tip güvenliği testini Python kabuğunuza yapıştırın:
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')
Test assertRaises
, bir bağlam yöneticisi olarak kullanılır, bu da kaydedildiğinde hatanın düzgün bir şekilde yakalanmasını ve temizlenmesini sağlar.
İçerik yöneticisi olmadan da yazabiliriz , bkz. İkinci test. İlk argüman yükseltmeyi düşündüğünüz hata türü, ikinci argüman, test ettiğiniz fonksiyon ve kalan argümanlar ve anahtar kelime argümanları bu fonksiyona aktarılacaktır.
Sadece bağlam yöneticisini kullanmanın çok daha basit, okunabilir ve bakımı kolay olduğunu düşünüyorum.
Testleri yapmak için:
unittest.main(exit=False)
Python 2.6'da muhtemelen aşağıdakilere ihtiyacınız olacak :
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Ve terminaliniz aşağıdakileri çıkarmalıdır:
..
----------------------------------------------------------------------
Ran 2 tests in 0.007s
OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>
Ve beklediğimiz gibi, a 1
ve bir '1'
sonuç eklemeye çalıştığımızı görüyoruz TypeError
.
Daha ayrıntılı çıktı için şunu deneyin:
unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Kodunuz bu kalıba uymalıdır (bu bir unittest modülü stil testidir):
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'de bu yapı, beklenen istisnada belirli değerleri kontrol etmek için kullanışlıdır. Unittest işlevi assertRaises
yalnızca bir istisnanın oluşup oluşmadığını kontrol eder.
assertRaises
alırsanız bir BAŞARISIZ yerine bir HATA alacağınızdır.
from: http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/
İlk olarak, dum_function.py dosyasındaki karşılık gelen (hala dum: p) işlevi şunlardır:
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
Yapılacak test (sadece bu test eklenir):
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()
Artık fonksiyonumuzu test etmeye hazırız! Testi çalıştırmaya çalışırken aşağıdakiler gerçekleşir:
======================================================================
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 harekete geçirilir ve bir test hatası oluşturur. Sorun şu ki tam olarak istediğimiz davranış budur: s.
Bu hatayı önlemek için, test çağrısında lambda kullanarak işlevi çalıştırın:
self.assertRaises(TypeError, lambda: df.square_value(self.false_int))
Son çıktı:
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Mükemmel !
... ve benim için de mükemmel !!
Çok fazla Thansk Bay Julien Lengrand-Lambert
Bu test iddiası aslında yanlış bir pozitif döndürür . Bunun nedeni, 'assertRaises' içindeki lambda'nın , test edilen işlevi değil , tür hatasını yükselten birim olmasıdır .
self.assertRaises(TypeError, df.square_value(self.false_int))
yöntemi çağırır ve sonucu döndürür. İstediğiniz yöntemi ve herhangi bir argümanı geçmek ve self.assertRaises(TypeError, df.square_value, self.false_int)
contextmanager
İstisnanın ortaya çıkıp çıkmadığını kontrol etmek için kendiniz bir tane oluşturabilirsiniz.
import contextlib
@contextlib.contextmanager
def raises(exception):
try:
yield
except exception as e:
assert True
else:
assert False
Ve sonra şöyle kullanabilirsiniz raises
:
with raises(Exception):
print "Hola" # Calls assert False
with raises(Exception):
raise Exception # Calls assert True
Eğer kullanıyorsanız pytest
, bu şey zaten uygulanmıştır. Şunları yapabilirsiniz pytest.raises(Exception)
:
Misal:
def test_div_zero():
with pytest.raises(ZeroDivisionError):
1/0
Ve sonuç:
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
Modülü gerektirmeyen bir cevap gönderdiğiniz için teşekkür ederiz !
Kullandığım doctest.testmodHemen her yerde [1] çünkü fonksiyonlarımı aynı anda belgelendirip test seviyorum.
Bu koda bir göz atın:
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()
Bu örneği bir modüle koyar ve komut satırından çalıştırırsanız, her iki test durumu da değerlendirilir ve kontrol edilir.
[1] Python belgeleri: 23.2 doctest - Etkileşimli Python örneklerini test edin
Sadece Mock kütüphanesinin sadece beklenen istisnanın yükseltildiğini değil, aynı zamanda beklenen mesajla da yükseltildiğini kontrol edecek bir assertRaisesWithMessage () yöntemi (unittest.TestCase alt sınıfında) sağladığını keşfettim :
from testcase import TestCase
import mymod
class MyTestCase(TestCase):
def test1(self):
self.assertRaisesWithMessage(SomeCoolException,
'expected message',
mymod.myfunc)
Burada birçok cevap var. Kod, nasıl bir İstisna oluşturabileceğimizi, bu istisnayı yöntemlerimizde nasıl kullanabileceğimizi ve son olarak, bir birim testinde nasıl doğrulayabileceğinizi, doğru istisnaları ortaya koyduğunu gösterir.
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 modülünden assertRaises kullanabilirsiniz
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)
Tüm cevaplar gayet iyi olsa da, bir fonksiyonun birim test çerçevelerine dayanmadan ve test sınıfları yazmak zorunda kalmadan bir istisna getirip getirmediğini test etmenin bir yolunu arıyordum.
Sonunda aşağıdakileri yazdım:
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)
Ve doğru yolda başarısız olur:
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