225 lines
8.4 KiB
Python
225 lines
8.4 KiB
Python
"""
|
|
This is a backport of assertRaises() and assertRaisesRegex from Python 3.5.4
|
|
|
|
The original copyright message is as follows
|
|
|
|
Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's
|
|
Smalltalk testing framework (used with permission).
|
|
|
|
This module contains the core framework classes that form the basis of
|
|
specific test cases and suites (TestCase, TestSuite etc.), and also a
|
|
text-based utility class for running the tests and reporting the results
|
|
(TextTestRunner).
|
|
|
|
Simple usage:
|
|
|
|
import unittest
|
|
|
|
class IntegerArithmeticTestCase(unittest.TestCase):
|
|
def testAdd(self): # test method names begin with 'test'
|
|
self.assertEqual((1 + 2), 3)
|
|
self.assertEqual(0 + 1, 1)
|
|
def testMultiply(self):
|
|
self.assertEqual((0 * 10), 0)
|
|
self.assertEqual((5 * 8), 40)
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|
|
|
|
Further information is available in the bundled documentation, and from
|
|
|
|
https://docs.python.org/library/unittest.html
|
|
|
|
Copyright (c) 1999-2003 Steve Purcell
|
|
Copyright (c) 2003-2010 Python Software Foundation
|
|
This module is free software, and you may redistribute it and/or modify
|
|
it under the same terms as Python itself, so long as this copyright message
|
|
and disclaimer are retained in their original form.
|
|
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
|
|
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
|
|
THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
DAMAGE.
|
|
|
|
THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
|
|
AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
|
|
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
"""
|
|
|
|
import re
|
|
import warnings
|
|
import unittest
|
|
|
|
|
|
def _is_subtype(expected, basetype):
|
|
if isinstance(expected, tuple):
|
|
return all(_is_subtype(e, basetype) for e in expected)
|
|
return isinstance(expected, type) and issubclass(expected, basetype)
|
|
|
|
|
|
class _BaseTestCaseContext:
|
|
|
|
def __init__(self, test_case):
|
|
self.test_case = test_case
|
|
|
|
def _raiseFailure(self, standardMsg):
|
|
msg = self.test_case._formatMessage(self.msg, standardMsg)
|
|
raise self.test_case.failureException(msg)
|
|
|
|
|
|
class _AssertRaisesBaseContext(_BaseTestCaseContext):
|
|
|
|
def __init__(self, expected, test_case, expected_regex=None):
|
|
_BaseTestCaseContext.__init__(self, test_case)
|
|
self.expected = expected
|
|
self.test_case = test_case
|
|
if expected_regex is not None:
|
|
expected_regex = re.compile(expected_regex)
|
|
self.expected_regex = expected_regex
|
|
self.obj_name = None
|
|
self.msg = None
|
|
|
|
def handle(self, name, args, kwargs):
|
|
"""
|
|
If args is empty, assertRaises/Warns is being used as a
|
|
context manager, so check for a 'msg' kwarg and return self.
|
|
If args is not empty, call a callable passing positional and keyword
|
|
arguments.
|
|
"""
|
|
try:
|
|
if not _is_subtype(self.expected, self._base_type):
|
|
raise TypeError('%s() arg 1 must be %s' %
|
|
(name, self._base_type_str))
|
|
if args and args[0] is None:
|
|
warnings.warn("callable is None",
|
|
DeprecationWarning, 3)
|
|
args = ()
|
|
if not args:
|
|
self.msg = kwargs.pop('msg', None)
|
|
if kwargs:
|
|
warnings.warn('%r is an invalid keyword argument for '
|
|
'this function' % next(iter(kwargs)),
|
|
DeprecationWarning, 3)
|
|
return self
|
|
|
|
callable_obj, args = args[0], args[1:]
|
|
try:
|
|
self.obj_name = callable_obj.__name__
|
|
except AttributeError:
|
|
self.obj_name = str(callable_obj)
|
|
with self:
|
|
callable_obj(*args, **kwargs)
|
|
finally:
|
|
# bpo-23890: manually break a reference cycle
|
|
self = None
|
|
|
|
|
|
class _AssertRaisesContext(_AssertRaisesBaseContext):
|
|
"""A context manager used to implement TestCase.assertRaises* methods."""
|
|
|
|
_base_type = BaseException
|
|
_base_type_str = 'an exception type or tuple of exception types'
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_value, tb):
|
|
if exc_type is None:
|
|
try:
|
|
exc_name = self.expected.__name__
|
|
except AttributeError:
|
|
exc_name = str(self.expected)
|
|
if self.obj_name:
|
|
self._raiseFailure("{} not raised by {}".format(exc_name,
|
|
self.obj_name))
|
|
else:
|
|
self._raiseFailure("{} not raised".format(exc_name))
|
|
if not issubclass(exc_type, self.expected):
|
|
return False
|
|
if self.expected_regex is None:
|
|
return True
|
|
|
|
expected_regex = self.expected_regex
|
|
if not expected_regex.search(str(exc_value)):
|
|
self._raiseFailure('"{}" does not match "{}"'.format(
|
|
expected_regex.pattern, str(exc_value)))
|
|
return True
|
|
|
|
|
|
class TestCase(unittest.TestCase):
|
|
longMessage = True
|
|
failureException = AssertionError
|
|
|
|
def _formatMessage(self, msg, standardMsg):
|
|
"""Honour the longMessage attribute when generating failure messages.
|
|
If longMessage is False this means:
|
|
* Use only an explicit message if it is provided
|
|
* Otherwise use the standard message for the assert
|
|
|
|
If longMessage is True:
|
|
* Use the standard message
|
|
* If an explicit message is provided, plus ' : ' and the explicit msg
|
|
"""
|
|
if not self.longMessage:
|
|
return msg or standardMsg
|
|
if msg is None:
|
|
return standardMsg
|
|
try:
|
|
# don't switch to '{}' formatting in Python 2.X
|
|
# it changes the way unicode input is handled
|
|
return '%s : %s' % (standardMsg, msg)
|
|
except UnicodeDecodeError:
|
|
return '%s : %s' % (standardMsg, msg)
|
|
|
|
def assertRaises(self, expected_exception, *args, **kwargs):
|
|
"""Fail unless an exception of class expected_exception is raised
|
|
by the callable when invoked with specified positional and
|
|
keyword arguments. If a different type of exception is
|
|
raised, it will not be caught, and the test case will be
|
|
deemed to have suffered an error, exactly as for an
|
|
unexpected exception.
|
|
|
|
If called with the callable and arguments omitted, will return a
|
|
context object used like this::
|
|
|
|
with self.assertRaises(SomeException):
|
|
do_something()
|
|
|
|
An optional keyword argument 'msg' can be provided when assertRaises
|
|
is used as a context object.
|
|
|
|
The context manager keeps a reference to the exception as
|
|
the 'exception' attribute. This allows you to inspect the
|
|
exception after the assertion::
|
|
|
|
with self.assertRaises(SomeException) as cm:
|
|
do_something()
|
|
the_exception = cm.exception
|
|
self.assertEqual(the_exception.error_code, 3)
|
|
"""
|
|
context = _AssertRaisesContext(expected_exception, self)
|
|
try:
|
|
return context.handle('assertRaises', args, kwargs)
|
|
finally:
|
|
# bpo-23890: manually break a reference cycle
|
|
context = None
|
|
|
|
def assertRaisesRegex(self, expected_exception,
|
|
expected_regex, *args, **kwargs):
|
|
"""Asserts that the message in a raised exception matches a regex.
|
|
|
|
Args:
|
|
expected_exception: Exception class expected to be raised.
|
|
expected_regex: Regex (re pattern object or string) expected
|
|
to be found in error message.
|
|
args: Function to be called and extra positional args.
|
|
kwargs: Extra kwargs.
|
|
msg: Optional message used in case of failure. Can only be used
|
|
when assertRaisesRegex is used as a context manager.
|
|
"""
|
|
context = _AssertRaisesContext(expected_exception,
|
|
self, expected_regex)
|
|
return context.handle('assertRaisesRegex', args, kwargs)
|