Bagaimana seseorang menulis unittest yang gagal hanya jika suatu fungsi tidak membuang pengecualian yang diharapkan?
Bagaimana seseorang menulis unittest yang gagal hanya jika suatu fungsi tidak membuang pengecualian yang diharapkan?
Jawaban:
Gunakan TestCase.assertRaises
(atau TestCase.failUnlessRaises
) dari modul unittest, misalnya:
import mymod
class MyTestCase(unittest.TestCase):
def test1(self):
self.assertRaises(SomeCoolException, mymod.myfunc)
myfunc
Anda harus menambahkannya sebagai argumen untuk panggilan assertRaises. Lihat jawaban Daryl Spitzer.
self.assertRaises(TypeError, mymod.myfunc)
. Anda dapat menemukan daftar lengkap Pengecualian Bawaan
self.assertRaises(SomeCoolException, Constructor, arg1)
Karena Python 2.7 Anda dapat menggunakan manajer konteks untuk mengetahui objek Exception yang sebenarnya dilemparkan:
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
Dalam Python 3.5 , Anda harus membungkus context.exception
di str
, jika tidak, anda akan mendapatkanTypeError
self.assertTrue('This is broken' in str(context.exception))
context.exception
tidak memberikan pesan; itu adalah tipe.
import unittest2
, Anda perlu menggunakan str()
, yaitu self.assertTrue('This is broken' in str(context.exception))
.
Kode dalam jawaban saya sebelumnya dapat disederhanakan menjadi:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction)
Dan jika fungsi mengambil argumen, cukup berikan mereka ke assertRaises seperti ini:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction, arg1, arg2)
2.7.15
. Jika afunction
in self.assertRaises(ExpectedException, afunction, arg1, arg2)
adalah inisialisasi kelas, Anda harus memberikan self
argumen pertama misalnya, self.assertRaises(ExpectedException, Class, self, arg1, arg2)
Bagaimana Anda menguji bahwa fungsi Python melempar pengecualian?
Bagaimana seseorang menulis tes yang gagal hanya jika suatu fungsi tidak melempar pengecualian yang diharapkan?
Gunakan self.assertRaises
metode ini sebagai manajer konteks:
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
Pendekatan praktik terbaik cukup mudah untuk ditunjukkan dalam shell Python.
The unittest
library
Dengan Python 2.7 atau 3:
import unittest
Dalam Python 2.6, Anda dapat menginstal backport unittest
perpustakaan 2,7 , disebut unittest2 , dan hanya itu sebagai unittest
:
import unittest2 as unittest
Sekarang, tempel ke dalam kulit Python Anda tes berikut tentang keamanan tipe 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')
Uji satu menggunakan assertRaises
sebagai manajer konteks, yang memastikan bahwa kesalahan ditangkap dan dibersihkan dengan benar, saat direkam.
Kita juga bisa menulisnya tanpa manajer konteks, lihat tes dua. Argumen pertama akan menjadi tipe kesalahan yang Anda harapkan untuk naik, argumen kedua, fungsi yang Anda uji, dan args yang tersisa serta args kata kunci akan diteruskan ke fungsi itu.
Saya pikir itu jauh lebih sederhana, mudah dibaca, dan dipelihara hanya dengan menggunakan manajer konteks.
Untuk menjalankan tes:
unittest.main(exit=False)
Dalam Python 2.6, Anda mungkin perlu yang berikut :
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Dan terminal Anda harus menampilkan yang berikut:
..
----------------------------------------------------------------------
Ran 2 tests in 0.007s
OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>
Dan kami melihat itu seperti yang kami harapkan, berusaha untuk menambahkan 1
dan '1'
menghasilkan dalam TypeError
.
Untuk lebih banyak keluaran verbose, coba ini:
unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Kode Anda harus mengikuti pola ini (ini adalah tes gaya modul yang paling bebas):
def test_afunction_throws_exception(self):
try:
afunction()
except ExpectedException:
pass
except Exception:
self.fail('unexpected exception raised')
else:
self.fail('ExpectedException not raised')
Pada Python <2.7 konstruk ini berguna untuk memeriksa nilai-nilai spesifik dalam pengecualian yang diharapkan. Fungsi unittest assertRaises
hanya memeriksa apakah pengecualian muncul.
assertRaises
Anda akan mendapatkan KESALAHAN bukannya GAGAL.
dari: http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/
Pertama, berikut adalah fungsi yang sesuai (masih dum: p) dalam file dum_function.py:
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
Inilah tes yang harus dilakukan (hanya tes ini yang dimasukkan):
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()
Kami sekarang siap untuk menguji fungsi kami! Inilah yang terjadi ketika mencoba menjalankan tes:
======================================================================
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 dinaikkan, dan menghasilkan kegagalan pengujian. Masalahnya adalah inilah perilaku yang kita inginkan: s.
Untuk menghindari kesalahan ini, cukup jalankan fungsi menggunakan lambda di panggilan tes:
self.assertRaises(TypeError, lambda: df.square_value(self.false_int))
Hasil akhir:
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Sempurna!
... dan bagiku juga sempurna !!
Thansk banyak Pak Julien Lengrand-Lambert
Tes ini menegaskan sebenarnya mengembalikan false positive . Itu terjadi karena lambda di dalam 'assertRaises' adalah unit yang memunculkan kesalahan tipe dan bukan fungsi yang diuji.
self.assertRaises(TypeError, df.square_value(self.false_int))
memanggil metode dan mengembalikan hasilnya. Yang Anda inginkan adalah untuk lulus metode dan argumen apa pun dan biarkan yang paling tidak layak untuk menyebutnya:self.assertRaises(TypeError, df.square_value, self.false_int)
Anda dapat membuat sendiri contextmanager
untuk memeriksa apakah pengecualian telah dinaikkan.
import contextlib
@contextlib.contextmanager
def raises(exception):
try:
yield
except exception as e:
assert True
else:
assert False
Dan kemudian Anda dapat menggunakan raises
seperti ini:
with raises(Exception):
print "Hola" # Calls assert False
with raises(Exception):
raise Exception # Calls assert True
Jika Anda menggunakan pytest
, hal ini sudah diterapkan. Anda dapat melakukanpytest.raises(Exception)
:
Contoh:
def test_div_zero():
with pytest.raises(ZeroDivisionError):
1/0
Dan hasilnya:
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
modul!
Saya menggunakan doctest [1] hampir di mana-mana karena saya suka fakta bahwa saya mendokumentasikan dan menguji fungsi saya pada saat yang sama.
Lihatlah kode ini:
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()
Jika Anda meletakkan contoh ini dalam modul dan menjalankannya dari baris perintah, kedua kasus uji dievaluasi dan diperiksa.
[1] Dokumentasi Python: 23.2 doctest - Uji contoh Python interaktif
Saya baru saja menemukan bahwa perpustakaan Mock menyediakan metode assertRaisesWithMessage () (dalam subclass unittest.TestCase), yang akan memeriksa tidak hanya bahwa pengecualian yang diharapkan dinaikkan, tetapi juga yang dimunculkan dengan pesan yang diharapkan:
from testcase import TestCase
import mymod
class MyTestCase(TestCase):
def test1(self):
self.assertRaisesWithMessage(SomeCoolException,
'expected message',
mymod.myfunc)
Ada banyak jawaban di sini. Kode menunjukkan bagaimana kita dapat membuat Pengecualian, bagaimana kita dapat menggunakan pengecualian itu dalam metode kami, dan akhirnya, bagaimana Anda dapat memverifikasi dalam pengujian unit, pengecualian yang benar dimunculkan.
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()
Anda dapat menggunakan assertRaises dari modul unittest
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)
Sementara semua jawaban baik-baik saja, saya mencari cara untuk menguji apakah suatu fungsi menimbulkan pengecualian tanpa bergantung pada kerangka kerja unit testing dan harus menulis kelas tes.
Saya akhirnya menulis yang berikut ini:
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)
Dan gagal di jalur yang benar:
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