Làm thế nào để người ta viết một lỗi nhỏ nhất mà chỉ thất bại nếu một hàm không đưa ra một ngoại lệ dự kiến?
Làm thế nào để người ta viết một lỗi nhỏ nhất mà chỉ thất bại nếu một hàm không đưa ra một ngoại lệ dự kiến?
Câu trả lời:
Sử dụng TestCase.assertRaises
(hoặc TestCase.failUnlessRaises
) từ mô-đun nhỏ nhất, ví dụ:
import mymod
class MyTestCase(unittest.TestCase):
def test1(self):
self.assertRaises(SomeCoolException, mymod.myfunc)
myfunc
bạn cần thêm chúng làm đối số cho lệnh gọi assertRaises. Xem câu trả lời của Daryl Spitzer.
self.assertRaises(TypeError, mymod.myfunc)
. Bạn có thể tìm thấy một danh sách đầy đủ các Ngoại lệ Built-in ở đây: docs.python.org/3/library/exceptions.html#bltin-exceptions
self.assertRaises(SomeCoolException, Constructor, arg1)
Vì Python 2.7, bạn có thể sử dụng trình quản lý bối cảnh để nhận được đối tượng Exception thực tế bị ném:
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/l Library / unittest.html # unittest.TestCase.assertRaises
Trong Python 3.5 , bạn phải quấn context.exception
trong str
, nếu không bạn sẽ nhận được mộtTypeError
self.assertTrue('This is broken' in str(context.exception))
context.exception
không đưa ra thông điệp; nó là một loại
import unittest2
, bạn cần sử dụng str()
, tức là self.assertTrue('This is broken' in str(context.exception))
.
Mã trong câu trả lời trước của tôi có thể được đơn giản hóa thành:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction)
Và nếu giao thức có các đối số, chỉ cần chuyển chúng vào các xác nhận như thế này:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction, arg1, arg2)
2.7.15
. Nếu afunction
trong self.assertRaises(ExpectedException, afunction, arg1, arg2)
là trình khởi tạo lớp, bạn cần chuyển qua self
làm đối số đầu tiên, ví dụ: self.assertRaises(ExpectedException, Class, self, arg1, arg2)
Làm thế nào để bạn kiểm tra rằng một hàm Python ném một ngoại lệ?
Làm thế nào để người ta viết một bài kiểm tra thất bại chỉ khi một hàm không đưa ra một ngoại lệ dự kiến?
Sử dụng self.assertRaises
phương thức làm trình quản lý bối cảnh:
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
Cách tiếp cận thực hành tốt nhất là khá dễ dàng để chứng minh trong trình bao Python.
các unittest
thư viện
Trong Python 2.7 hoặc 3:
import unittest
Trong Python 2.6, bạn có thể cài đặt một backport của unittest
thư viện 2.7 , được gọi là unittest2 và chỉ là bí danh như unittest
:
import unittest2 as unittest
Bây giờ, dán vào trình bao Python của bạn trong bài kiểm tra sau về độ an toàn của 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')
Kiểm tra một sử dụng assertRaises
như một trình quản lý bối cảnh, đảm bảo rằng lỗi được bắt đúng và được dọn sạch, trong khi được ghi lại.
Chúng tôi cũng có thể viết nó mà không cần trình quản lý bối cảnh, xem thử nghiệm hai. Đối số đầu tiên sẽ là loại lỗi bạn mong muốn nêu ra, đối số thứ hai, chức năng bạn đang kiểm tra và các đối số và đối số từ khóa còn lại sẽ được chuyển đến chức năng đó.
Tôi nghĩ rằng nó đơn giản hơn nhiều, có thể đọc và duy trì để sử dụng trình quản lý bối cảnh.
Để chạy thử nghiệm:
unittest.main(exit=False)
Trong Python 2.6, có thể bạn sẽ cần những điều sau :
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Và thiết bị đầu cuối của bạn sẽ xuất ra như sau:
..
----------------------------------------------------------------------
Ran 2 tests in 0.007s
OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>
Và chúng ta thấy rằng như chúng ta mong đợi, cố gắng thêm một 1
và '1'
kết quả trong một TypeError
.
Để biết thêm đầu ra dài dòng, hãy thử điều này:
unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Mã của bạn phải tuân theo mẫu này (đây là thử nghiệm kiểu mô-đun không đáng tin cậy nhất):
def test_afunction_throws_exception(self):
try:
afunction()
except ExpectedException:
pass
except Exception:
self.fail('unexpected exception raised')
else:
self.fail('ExpectedException not raised')
Trên Python <2.7 cấu trúc này rất hữu ích để kiểm tra các giá trị cụ thể trong ngoại lệ dự kiến. Hàm unittest assertRaises
chỉ kiểm tra nếu một ngoại lệ được đưa ra.
assertRaises
bạn sẽ nhận được LRI thay vì FAIL.
từ: http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/
Đầu tiên, đây là hàm tương ứng (still dum: p) trong tệp dum_feft.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
Đây là bài kiểm tra cần thực hiện (chỉ bài kiểm tra này được chèn):
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()
Bây giờ chúng tôi đã sẵn sàng để kiểm tra chức năng của chúng tôi! Đây là những gì xảy ra khi cố gắng chạy thử nghiệm:
======================================================================
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 được nâng cấp và tạo ra lỗi thử nghiệm. Vấn đề là đây chính xác là hành vi mà chúng tôi muốn: s.
Để tránh lỗi này, chỉ cần chạy chức năng bằng lambda trong cuộc gọi thử nghiệm:
self.assertRaises(TypeError, lambda: df.square_value(self.false_int))
Đầu ra cuối cùng:
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Hoàn hảo !
... Và đối với tôi cũng hoàn hảo !!
Rất nhiều ông Julien Lengrand-Lambert
Thử nghiệm này khẳng định thực sự trả về một dương tính giả . Điều đó xảy ra bởi vì lambda bên trong 'assertRaises' là đơn vị làm tăng lỗi loại và không phải là chức năng được kiểm tra.
self.assertRaises(TypeError, df.square_value(self.false_int))
gọi phương thức và trả về kết quả. Những gì bạn muốn là vượt qua phương thức và bất kỳ đối số nào và để cho người ít nhất gọi nó:self.assertRaises(TypeError, df.square_value, self.false_int)
Bạn có thể tự xây dựng contextmanager
để kiểm tra xem ngoại lệ có được nêu ra không.
import contextlib
@contextlib.contextmanager
def raises(exception):
try:
yield
except exception as e:
assert True
else:
assert False
Và sau đó bạn có thể sử dụng raises
như thế này:
with raises(Exception):
print "Hola" # Calls assert False
with raises(Exception):
raise Exception # Calls assert True
Nếu bạn đang sử dụng pytest
, điều này đã được thực hiện. Bạn có thể làm pytest.raises(Exception)
:
Thí dụ:
def test_div_zero():
with pytest.raises(ZeroDivisionError):
1/0
Và kết quả:
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
mô-đun!
Tôi sử dụng doctest [1] gần như ở mọi nơi vì tôi thích thực tế là tôi tài liệu và kiểm tra các chức năng của mình cùng một lúc.
Hãy xem mã này:
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()
Nếu bạn đặt ví dụ này trong một mô-đun và chạy nó từ dòng lệnh, cả hai trường hợp kiểm tra đều được ước tính và kiểm tra.
[1] Tài liệu Python: 23.2 doctest - Kiểm tra các ví dụ Python tương tác
Tôi vừa phát hiện ra rằng thư viện Mock cung cấp một phương thức assertRaisesWithMessage () (trong lớp con unittest.TestCase của nó), nó sẽ kiểm tra không chỉ ngoại lệ dự kiến được đưa ra, mà còn được nêu ra với thông điệp dự kiến:
from testcase import TestCase
import mymod
class MyTestCase(TestCase):
def test1(self):
self.assertRaisesWithMessage(SomeCoolException,
'expected message',
mymod.myfunc)
Có rất nhiều câu trả lời ở đây. Mã này cho thấy cách chúng tôi có thể tạo Ngoại lệ, cách chúng tôi có thể sử dụng ngoại lệ đó trong các phương thức của mình và cuối cùng, cách bạn có thể xác minh trong thử nghiệm đơn vị, các ngoại lệ chính xác được nêu ra.
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()
Bạn có thể sử dụng assertRaises từ mô-đun nhỏ nhất
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)
Trong khi tất cả các câu trả lời là hoàn toàn tốt, tôi đã tìm cách để kiểm tra xem một hàm có đưa ra một ngoại lệ mà không dựa vào các khung kiểm thử đơn vị và phải viết các lớp kiểm tra hay không.
Cuối cùng tôi đã viết như sau:
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)
Và nó thất bại ở đúng dòng:
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