summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/unittest.rst267
-rw-r--r--Lib/test/test_gc.py2
-rw-r--r--Lib/test/test_struct.py1
-rw-r--r--Lib/test/test_unittest.py633
-rw-r--r--Lib/unittest.py607
5 files changed, 1376 insertions, 134 deletions
diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst
index c9dc6b6..5034ed4 100644
--- a/Doc/library/unittest.rst
+++ b/Doc/library/unittest.rst
@@ -1,4 +1,3 @@
-
:mod:`unittest` --- Unit testing framework
==========================================
@@ -207,8 +206,8 @@ The simplest :class:`TestCase` subclass will simply override the
widget = Widget('The widget')
self.assertEqual(widget.size(), (50, 50), 'incorrect default size')
-Note that in order to test something, we use the one of the :meth:`assert\*` or
-:meth:`fail\*` methods provided by the :class:`TestCase` base class. If the
+Note that in order to test something, we use the one of the :meth:`assert\*`
+methods provided by the :class:`TestCase` base class. If the
test fails, an exception will be raised, and :mod:`unittest` will identify the
test case as a :dfn:`failure`. Any other exceptions will be treated as
:dfn:`errors`. This helps you identify where the problem is: :dfn:`failures` are
@@ -238,13 +237,13 @@ us when we run the test::
class DefaultWidgetSizeTestCase(SimpleWidgetTestCase):
def runTest(self):
- self.failUnless(self.widget.size() == (50,50),
+ self.assertTrue(self.widget.size() == (50,50),
'incorrect default size')
class WidgetResizeTestCase(SimpleWidgetTestCase):
def runTest(self):
self.widget.resize(100,150)
- self.failUnless(self.widget.size() == (100,150),
+ self.assertTrue(self.widget.size() == (100,150),
'wrong size after resize')
If the :meth:`~TestCase.setUp` method raises an exception while the test is
@@ -286,12 +285,12 @@ mechanism::
self.widget = None
def testDefaultSize(self):
- self.failUnless(self.widget.size() == (50,50),
+ self.assertTrue(self.widget.size() == (50,50),
'incorrect default size')
def testResize(self):
self.widget.resize(100,150)
- self.failUnless(self.widget.size() == (100,150),
+ self.assertTrue(self.widget.size() == (100,150),
'wrong size after resize')
Here we have not provided a :meth:`~TestCase.runTest` method, but have instead
@@ -605,23 +604,37 @@ Test cases
failures.
- .. method:: assert_(expr[, msg])
+ .. method:: assertTrue(expr[, msg])
+ assert_(expr[, msg])
failUnless(expr[, msg])
- assertTrue(expr[, msg])
Signal a test failure if *expr* is false; the explanation for the error
will be *msg* if given, otherwise it will be :const:`None`.
+ .. deprecated:: 2.7
+ :meth:`failUnless`.
+
.. method:: assertEqual(first, second[, msg])
failUnlessEqual(first, second[, msg])
Test that *first* and *second* are equal. If the values do not compare
equal, the test will fail with the explanation given by *msg*, or
- :const:`None`. Note that using :meth:`failUnlessEqual` improves upon
- doing the comparison as the first parameter to :meth:`failUnless`: the
- default value for *msg* can be computed to include representations of both
- *first* and *second*.
+ :const:`None`. Note that using :meth:`assertEqual` improves upon
+ doing the comparison as the first parameter to :meth:`assertTrue`: the
+ default value for *msg* include representations of both *first* and
+ *second*.
+
+ In addition, if *first* and *second* are the exact same type and one of
+ list, tuple, dict, set, or frozenset or any type that a subclass
+ registers :meth:`addTypeEqualityFunc` the type specific equality function
+ will be called in order to generate a more useful default error message.
+
+ .. versionchanged:: 2.7
+ Added the automatic calling of type specific equality function.
+
+ .. deprecated:: 2.7
+ :meth:`failUnlessEqual`.
.. method:: assertNotEqual(first, second[, msg])
@@ -629,11 +642,14 @@ Test cases
Test that *first* and *second* are not equal. If the values do compare
equal, the test will fail with the explanation given by *msg*, or
- :const:`None`. Note that using :meth:`failIfEqual` improves upon doing
- the comparison as the first parameter to :meth:`failUnless` is that the
+ :const:`None`. Note that using :meth:`assertNotEqual` improves upon doing
+ the comparison as the first parameter to :meth:`assertTrue` is that the
default value for *msg* can be computed to include representations of both
*first* and *second*.
+ .. deprecated:: 2.7
+ :meth:`failIfEqual`.
+
.. method:: assertAlmostEqual(first, second[, places[, msg]])
failUnlessAlmostEqual(first, second[, places[, msg]])
@@ -647,6 +663,9 @@ Test cases
compare equal, the test will fail with the explanation given by *msg*, or
:const:`None`.
+ .. deprecated:: 2.7
+ :meth:`failUnlessAlmostEqual`.
+
.. method:: assertNotAlmostEqual(first, second[, places[, msg]])
failIfAlmostEqual(first, second[, places[, msg]])
@@ -660,6 +679,128 @@ Test cases
compare equal, the test will fail with the explanation given by *msg*, or
:const:`None`.
+ .. deprecated:: 2.7
+ :meth:`failIfAlmostEqual`.
+
+
+ .. method:: assertGreater(first, second, msg=None)
+ assertGreaterEqual(first, second, msg=None)
+ assertLess(first, second, msg=None)
+ assertLessEqual(first, second, msg=None)
+
+ Test that *first* is respectively >, >=, < or <= than *second* depending
+ on the method name. If not, the test will fail with the nice explanation
+ or with the explanation given by *msg*::
+
+ >>> self.assertGreaterEqual(3, 4)
+ AssertionError: "3" unexpectedly not greater than or equal to "4"
+
+ .. versionadded:: 2.7
+
+
+ .. method:: assertMultiLineEqual(self, first, second, msg=None)
+
+ Test that the multiline string *first* is equal to the string *second*.
+ When not equal a diff of the two strings highlighting the differences
+ will be included in the error message.
+
+ If specified *msg* will be used as the error message on failure.
+
+ .. versionadded:: 2.7
+
+
+ .. method:: assertRegexpMatches(text, regexp[, msg=None]):
+
+ Verifies that a *regexp* search matches *text*. Fails with an error
+ message including the pattern and the *text*. *regexp* may be
+ a regular expression object or a string containing a regular expression
+ suitable for use by :func:`re.search`.
+
+ .. versionadded:: 2.7
+
+
+ .. method:: assertIn(first, second, msg=None)
+ assertNotIn(first, second, msg=None)
+
+ Tests that *first* is or is not in *second* with a nice explanitory error
+ message as appropriate.
+
+ If specified *msg* will be used as the error message on failure.
+
+ .. versionadded:: 2.7
+
+
+ .. method:: assertSameElements(expected, actual, msg=None)
+
+ Test that sequence *expected* contains the same elements as *actual*.
+ When they don't an error message listing the differences between the
+ sequences will be generated.
+
+ If specified *msg* will be used as the error message on failure.
+
+ .. versionadded:: 2.7
+
+
+ .. method:: assertSetEqual(set1, set2, msg=None)
+
+ Tests that two sets are equal. If not, an error message is constructed
+ that lists the differences between the sets.
+
+ Fails if either of *set1* or *set2* does not have a :meth:`set.difference`
+ method.
+
+ If specified *msg* will be used as the error message on failure.
+
+ .. versionadded:: 2.7
+
+
+ .. method:: assertDictEqual(expected, actual, msg=None)
+
+ Test that two dictionaries are equal. If not, an error message is
+ constructed that shows the differences in the dictionaries.
+
+ If specified *msg* will be used as the error message on failure.
+
+ .. versionadded:: 2.7
+
+
+ .. method:: assertDictContainsSubset(expected, actual, msg=None)
+
+ Tests whether the key value pairs in dictionary *actual* are a
+ superset of those in *expected*. If not, an error message listing
+ the missing keys and mismatched values is generated.
+
+ If specified *msg* will be used as the error message on failure.
+
+ .. versionadded:: 2.7
+
+
+ .. method:: assertListEqual(list1, list2, msg=None)
+ assertTupleEqual(tuple1, tuple2, msg=None)
+
+ Tests that two lists or tuples are equal. If not an error message is
+ constructed that shows only the differences between the two. An error
+ is also raised if either of the parameters are of the wrong type.
+
+ If specified *msg* will be used as the error message on failure.
+
+ .. versionadded:: 2.7
+
+
+ .. method:: assertSequenceEqual(seq1, seq2, msg=None, seq_type=None)
+
+ Tests that two sequences are equal. If a *seq_type* is supplied, both
+ *seq1* and *seq2* must be instances of *seq_type* or a failure will
+ be raised. If the sequences are different an error message is
+ constructed that shows the difference between the two.
+
+ If specified *msg* will be used as the error message on failure.
+
+ This method is used to implement :meth:`assertListEqual` and
+ :meth:`assertTupleEqual`.
+
+ .. versionadded:: 2.7
+
.. method:: assertRaises(exception[, callable, ...])
failUnlessRaises(exception[, callable, ...])
@@ -680,14 +821,53 @@ Test cases
.. versionchanged:: 3.1
Added the ability to use :meth:`assertRaises` as a context manager.
+ .. deprecated:: 2.7
+ :meth:`failUnlessRaises`.
+
+
+ .. method:: assertRaisesRegexp(exception, regexp[, callable, ...])
+
+ Like :meth:`assertRaises` but also tests that *regexp* matches
+ on the string representation of the raised exception. *regexp* may be
+ a regular expression object or a string containing a regular expression
+ suitable for use by :func:`re.search`. Examples::
+
+ self.assertRaisesRegexp(ValueError, 'invalid literal for.*XYZ$',
+ int, 'XYZ')
+
+ or::
+
+ with self.assertRaisesRegexp(ValueError, 'literal'):
+ int('XYZ')
+
+ .. versionadded:: 2.7
+
+
+ .. method:: assertIsNone(expr[, msg])
- .. method:: failIf(expr[, msg])
- assertFalse(expr[, msg])
+ This signals a test failure if *expr* is not None.
- The inverse of the :meth:`failUnless` method is the :meth:`failIf` method.
+ .. versionadded:: 2.7
+
+
+ .. method:: assertIsNotNone(expr[, msg])
+
+ The inverse of the :meth:`assertIsNone` method.
+ This signals a test failure if *expr* is None.
+
+ .. versionadded:: 2.7
+
+
+ .. method:: assertFalse(expr[, msg])
+ failIf(expr[, msg])
+
+ The inverse of the :meth:`assertTrue` method is the :meth:`assertFalse` method.
This signals a test failure if *expr* is true, with *msg* or :const:`None`
for the error message.
+ .. deprecated:: 2.7
+ :meth:`failIf`.
+
.. method:: fail([msg])
@@ -703,6 +883,25 @@ Test cases
fair" with the framework. The initial value of this attribute is
:exc:`AssertionError`.
+
+ .. attribute:: longMessage
+
+ If set to True then any explicit failure message you pass in to the
+ assert methods will be appended to the end of the normal failure message.
+ The normal messages contain useful information about the objects involved,
+ for example the message from assertEqual shows you the repr of the two
+ unequal objects. Setting this attribute to True allows you to have a
+ custom error message in addition to the normal one.
+
+ This attribute defaults to False, meaning that a custom message passed
+ to an assert method will silence the normal message.
+
+ The class setting can be overridden in individual tests by assigning an
+ instance attribute to True or False before calling the assert methods.
+
+ .. versionadded:: 2.7
+
+
Testing frameworks can use the following methods to collect information on
the test:
@@ -732,10 +931,34 @@ Test cases
.. method:: shortDescription()
- Returns a one-line description of the test, or :const:`None` if no
- description has been provided. The default implementation of this method
- returns the first line of the test method's docstring, if available, or
- :const:`None`.
+ Returns a description of the test, or :const:`None` if no description
+ has been provided. The default implementation of this method
+ returns the first line of the test method's docstring, if available,
+ along with the method name.
+
+ .. versionchanged:: 2.7
+
+ In earlier versions this only returned the first line of the test
+ method's docstring, if available or the :const:`None`. That led to
+ undesirable behavior of not printing the test name when someone was
+ thoughtful enough to write a docstring.
+
+
+ .. method:: addTypeEqualityFunc(typeobj, function)
+
+ Registers a type specific :meth:`assertEqual` equality checking
+ function to be called by :meth:`assertEqual` when both objects it has
+ been asked to compare are exactly *typeobj* (not subclasses).
+ *function* must take two positional arguments and a third msg=None
+ keyword argument just as :meth:`assertEqual` does. It must raise
+ self.failureException when inequality between the first two
+ parameters is detected.
+
+ One good use of custom equality checking functions for a type
+ is to raise self.failureException with an error message useful
+ for debugging the by explaining the inequalities in detail.
+
+ .. versionadded:: 2.7
.. class:: FunctionTestCase(testFunc[, setUp[, tearDown[, description]]])
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index 2262b36..3b7df99 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -244,7 +244,7 @@ class GCTests(unittest.TestCase):
# - the call to assertEqual somehow avoids building its args tuple
def test_get_count(self):
# Avoid future allocation of method object
- assertEqual = self.assertEqual
+ assertEqual = self._baseAssertEqual
gc.collect()
assertEqual(gc.get_count(), (0, 0, 0))
a = dict()
diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py
index beeee8f..982047a 100644
--- a/Lib/test/test_struct.py
+++ b/Lib/test/test_struct.py
@@ -207,6 +207,7 @@ class StructTest(unittest.TestCase):
class IntTester(unittest.TestCase):
def __init__(self, formatpair, bytesize):
+ super(IntTester, self).__init__(methodName='test_one')
self.assertEqual(len(formatpair), 2)
self.formatpair = formatpair
for direction in "<>!=":
diff --git a/Lib/test/test_unittest.py b/Lib/test/test_unittest.py
index b7272f1..0782119 100644
--- a/Lib/test/test_unittest.py
+++ b/Lib/test/test_unittest.py
@@ -6,10 +6,12 @@ Still need testing:
TestCase.{assert,fail}* methods (some are tested implicitly)
"""
+import re
from test import support
import unittest
from unittest import TestCase
import types
+from copy import deepcopy
### Support code
################################################################
@@ -53,6 +55,8 @@ class LoggingResult(unittest.TestResult):
class TestEquality(object):
+ """Used as a mixin for TestCase"""
+
# Check for a valid __eq__ implementation
def test_eq(self):
for obj_1, obj_2 in self.eq_pairs:
@@ -66,25 +70,26 @@ class TestEquality(object):
self.failIfEqual(obj_2, obj_1)
class TestHashing(object):
+ """Used as a mixin for TestCase"""
+
# Check for a valid __hash__ implementation
def test_hash(self):
for obj_1, obj_2 in self.eq_pairs:
try:
- assert hash(obj_1) == hash(obj_2)
+ if not hash(obj_1) == hash(obj_2):
+ self.fail("%r and %r do not hash equal" % (obj_1, obj_2))
except KeyboardInterrupt:
raise
- except AssertionError:
- self.fail("%s and %s do not hash equal" % (obj_1, obj_2))
except Exception as e:
- self.fail("Problem hashing %s and %s: %s" % (obj_1, obj_2, e))
+ self.fail("Problem hashing %r and %r: %s" % (obj_1, obj_2, e))
for obj_1, obj_2 in self.ne_pairs:
try:
- assert hash(obj_1) != hash(obj_2)
+ if hash(obj_1) == hash(obj_2):
+ self.fail("%s and %s hash equal, but shouldn't" %
+ (obj_1, obj_2))
except KeyboardInterrupt:
raise
- except AssertionError:
- self.fail("%s and %s hash equal, but shouldn't" % (obj_1, obj_2))
except Exception as e:
self.fail("Problem hashing %s and %s: %s" % (obj_1, obj_2, e))
@@ -2247,39 +2252,6 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
self.failUnless(isinstance(Foo().id(), str))
- # "Returns a one-line description of the test, or None if no description
- # has been provided. The default implementation of this method returns
- # the first line of the test method's docstring, if available, or None."
- def test_shortDescription__no_docstring(self):
- class Foo(unittest.TestCase):
- def runTest(self):
- pass
-
- self.assertEqual(Foo().shortDescription(), None)
-
- # "Returns a one-line description of the test, or None if no description
- # has been provided. The default implementation of this method returns
- # the first line of the test method's docstring, if available, or None."
- def test_shortDescription__singleline_docstring(self):
- class Foo(unittest.TestCase):
- def runTest(self):
- "this tests foo"
- pass
-
- self.assertEqual(Foo().shortDescription(), "this tests foo")
-
- # "Returns a one-line description of the test, or None if no description
- # has been provided. The default implementation of this method returns
- # the first line of the test method's docstring, if available, or None."
- def test_shortDescription__multiline_docstring(self):
- class Foo(unittest.TestCase):
- def runTest(self):
- """this tests foo
- blah, bar and baz are also tested"""
- pass
-
- self.assertEqual(Foo().shortDescription(), "this tests foo")
-
# "If result is omitted or None, a temporary result object is created
# and used, but is not made available to the caller"
def test_run__uses_defaultTestResult(self):
@@ -2298,6 +2270,405 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
expected = ['startTest', 'test', 'addSuccess', 'stopTest']
self.assertEqual(events, expected)
+ def testShortDescriptionWithoutDocstring(self):
+ self.assertEqual(
+ self.shortDescription(),
+ 'testShortDescriptionWithoutDocstring (' + __name__ +
+ '.Test_TestCase)')
+
+ def testShortDescriptionWithOneLineDocstring(self):
+ """Tests shortDescription() for a method with a docstring."""
+ self.assertEqual(
+ self.shortDescription(),
+ ('testShortDescriptionWithOneLineDocstring '
+ '(' + __name__ + '.Test_TestCase)\n'
+ 'Tests shortDescription() for a method with a docstring.'))
+
+ def testShortDescriptionWithMultiLineDocstring(self):
+ """Tests shortDescription() for a method with a longer docstring.
+
+ This method ensures that only the first line of a docstring is
+ returned used in the short description, no matter how long the
+ whole thing is.
+ """
+ self.assertEqual(
+ self.shortDescription(),
+ ('testShortDescriptionWithMultiLineDocstring '
+ '(' + __name__ + '.Test_TestCase)\n'
+ 'Tests shortDescription() for a method with a longer '
+ 'docstring.'))
+
+ def testAddTypeEqualityFunc(self):
+ class SadSnake(object):
+ """Dummy class for test_addTypeEqualityFunc."""
+ s1, s2 = SadSnake(), SadSnake()
+ self.assertFalse(s1 == s2)
+ def AllSnakesCreatedEqual(a, b, msg=None):
+ return type(a) == type(b) == SadSnake
+ self.addTypeEqualityFunc(SadSnake, AllSnakesCreatedEqual)
+ self.assertEqual(s1, s2)
+ # No this doesn't clean up and remove the SadSnake equality func
+ # from this TestCase instance but since its a local nothing else
+ # will ever notice that.
+
+ def testAssertIn(self):
+ animals = {'monkey': 'banana', 'cow': 'grass', 'seal': 'fish'}
+
+ self.assertIn('a', 'abc')
+ self.assertIn(2, [1, 2, 3])
+ self.assertIn('monkey', animals)
+
+ self.assertNotIn('d', 'abc')
+ self.assertNotIn(0, [1, 2, 3])
+ self.assertNotIn('otter', animals)
+
+ self.assertRaises(self.failureException, self.assertIn, 'x', 'abc')
+ self.assertRaises(self.failureException, self.assertIn, 4, [1, 2, 3])
+ self.assertRaises(self.failureException, self.assertIn, 'elephant',
+ animals)
+
+ self.assertRaises(self.failureException, self.assertNotIn, 'c', 'abc')
+ self.assertRaises(self.failureException, self.assertNotIn, 1, [1, 2, 3])
+ self.assertRaises(self.failureException, self.assertNotIn, 'cow',
+ animals)
+
+ def testAssertDictContainsSubset(self):
+ self.assertDictContainsSubset({}, {})
+ self.assertDictContainsSubset({}, {'a': 1})
+ self.assertDictContainsSubset({'a': 1}, {'a': 1})
+ self.assertDictContainsSubset({'a': 1}, {'a': 1, 'b': 2})
+ self.assertDictContainsSubset({'a': 1, 'b': 2}, {'a': 1, 'b': 2})
+
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertDictContainsSubset, {'a': 2}, {'a': 1},
+ '.*Mismatched values:.*')
+
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertDictContainsSubset, {'c': 1}, {'a': 1},
+ '.*Missing:.*')
+
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertDictContainsSubset, {'a': 1, 'c': 1},
+ {'a': 1}, '.*Missing:.*')
+
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertDictContainsSubset, {'a': 1, 'c': 1},
+ {'a': 1}, '.*Missing:.*Mismatched values:.*')
+
+ def testAssertEqual(self):
+ equal_pairs = [
+ ((), ()),
+ ({}, {}),
+ ([], []),
+ (set(), set()),
+ (frozenset(), frozenset())]
+ for a, b in equal_pairs:
+ # This mess of try excepts is to test the assertEqual behavior
+ # itself.
+ try:
+ self.assertEqual(a, b)
+ except self.failureException:
+ self.fail('assertEqual(%r, %r) failed' % (a, b))
+ try:
+ self.assertEqual(a, b, msg='foo')
+ except self.failureException:
+ self.fail('assertEqual(%r, %r) with msg= failed' % (a, b))
+ try:
+ self.assertEqual(a, b, 'foo')
+ except self.failureException:
+ self.fail('assertEqual(%r, %r) with third parameter failed' %
+ (a, b))
+
+ unequal_pairs = [
+ ((), []),
+ ({}, set()),
+ (set([4,1]), frozenset([4,2])),
+ (frozenset([4,5]), set([2,3])),
+ (set([3,4]), set([5,4]))]
+ for a, b in unequal_pairs:
+ self.assertRaises(self.failureException, self.assertEqual, a, b)
+ self.assertRaises(self.failureException, self.assertEqual, a, b,
+ 'foo')
+ self.assertRaises(self.failureException, self.assertEqual, a, b,
+ msg='foo')
+
+ def testEquality(self):
+ self.assertListEqual([], [])
+ self.assertTupleEqual((), ())
+ self.assertSequenceEqual([], ())
+
+ a = [0, 'a', []]
+ b = []
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertListEqual, a, b)
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertListEqual, tuple(a), tuple(b))
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertSequenceEqual, a, tuple(b))
+
+ b.extend(a)
+ self.assertListEqual(a, b)
+ self.assertTupleEqual(tuple(a), tuple(b))
+ self.assertSequenceEqual(a, tuple(b))
+ self.assertSequenceEqual(tuple(a), b)
+
+ self.assertRaises(self.failureException, self.assertListEqual,
+ a, tuple(b))
+ self.assertRaises(self.failureException, self.assertTupleEqual,
+ tuple(a), b)
+ self.assertRaises(self.failureException, self.assertListEqual, None, b)
+ self.assertRaises(self.failureException, self.assertTupleEqual, None,
+ tuple(b))
+ self.assertRaises(self.failureException, self.assertSequenceEqual,
+ None, tuple(b))
+ self.assertRaises(self.failureException, self.assertListEqual, 1, 1)
+ self.assertRaises(self.failureException, self.assertTupleEqual, 1, 1)
+ self.assertRaises(self.failureException, self.assertSequenceEqual,
+ 1, 1)
+
+ self.assertDictEqual({}, {})
+
+ c = { 'x': 1 }
+ d = {}
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertDictEqual, c, d)
+
+ d.update(c)
+ self.assertDictEqual(c, d)
+
+ d['x'] = 0
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertDictEqual, c, d, 'These are unequal')
+
+ self.assertRaises(self.failureException, self.assertDictEqual, None, d)
+ self.assertRaises(self.failureException, self.assertDictEqual, [], d)
+ self.assertRaises(self.failureException, self.assertDictEqual, 1, 1)
+
+ self.assertSameElements([1, 2, 3], [3, 2, 1])
+ self.assertSameElements([1, 2] + [3] * 100, [1] * 100 + [2, 3])
+ self.assertSameElements(['foo', 'bar', 'baz'], ['bar', 'baz', 'foo'])
+ self.assertRaises(self.failureException, self.assertSameElements,
+ [10], [10, 11])
+ self.assertRaises(self.failureException, self.assertSameElements,
+ [10, 11], [10])
+
+ # Test that sequences of unhashable objects can be tested for sameness:
+ self.assertSameElements([[1, 2], [3, 4]], [[3, 4], [1, 2]])
+ self.assertRaises(self.failureException, self.assertSameElements,
+ [[1]], [[2]])
+
+ def testAssertSetEqual(self):
+ set1 = set()
+ set2 = set()
+ self.assertSetEqual(set1, set2)
+
+ self.assertRaises(self.failureException, self.assertSetEqual, None, set2)
+ self.assertRaises(self.failureException, self.assertSetEqual, [], set2)
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, None)
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, [])
+
+ set1 = set(['a'])
+ set2 = set()
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, set2)
+
+ set1 = set(['a'])
+ set2 = set(['a'])
+ self.assertSetEqual(set1, set2)
+
+ set1 = set(['a'])
+ set2 = set(['a', 'b'])
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, set2)
+
+ set1 = set(['a'])
+ set2 = frozenset(['a', 'b'])
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, set2)
+
+ set1 = set(['a', 'b'])
+ set2 = frozenset(['a', 'b'])
+ self.assertSetEqual(set1, set2)
+
+ set1 = set()
+ set2 = "foo"
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, set2)
+ self.assertRaises(self.failureException, self.assertSetEqual, set2, set1)
+
+ # make sure any string formatting is tuple-safe
+ set1 = set([(0, 1), (2, 3)])
+ set2 = set([(4, 5)])
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, set2)
+
+ def testInequality(self):
+ # Try ints
+ self.assertGreater(2, 1)
+ self.assertGreaterEqual(2, 1)
+ self.assertGreaterEqual(1, 1)
+ self.assertLess(1, 2)
+ self.assertLessEqual(1, 2)
+ self.assertLessEqual(1, 1)
+ self.assertRaises(self.failureException, self.assertGreater, 1, 2)
+ self.assertRaises(self.failureException, self.assertGreater, 1, 1)
+ self.assertRaises(self.failureException, self.assertGreaterEqual, 1, 2)
+ self.assertRaises(self.failureException, self.assertLess, 2, 1)
+ self.assertRaises(self.failureException, self.assertLess, 1, 1)
+ self.assertRaises(self.failureException, self.assertLessEqual, 2, 1)
+
+ # Try Floats
+ self.assertGreater(1.1, 1.0)
+ self.assertGreaterEqual(1.1, 1.0)
+ self.assertGreaterEqual(1.0, 1.0)
+ self.assertLess(1.0, 1.1)
+ self.assertLessEqual(1.0, 1.1)
+ self.assertLessEqual(1.0, 1.0)
+ self.assertRaises(self.failureException, self.assertGreater, 1.0, 1.1)
+ self.assertRaises(self.failureException, self.assertGreater, 1.0, 1.0)
+ self.assertRaises(self.failureException, self.assertGreaterEqual, 1.0, 1.1)
+ self.assertRaises(self.failureException, self.assertLess, 1.1, 1.0)
+ self.assertRaises(self.failureException, self.assertLess, 1.0, 1.0)
+ self.assertRaises(self.failureException, self.assertLessEqual, 1.1, 1.0)
+
+ # Try Strings
+ self.assertGreater('bug', 'ant')
+ self.assertGreaterEqual('bug', 'ant')
+ self.assertGreaterEqual('ant', 'ant')
+ self.assertLess('ant', 'bug')
+ self.assertLessEqual('ant', 'bug')
+ self.assertLessEqual('ant', 'ant')
+ self.assertRaises(self.failureException, self.assertGreater, 'ant', 'bug')
+ self.assertRaises(self.failureException, self.assertGreater, 'ant', 'ant')
+ self.assertRaises(self.failureException, self.assertGreaterEqual, 'ant', 'bug')
+ self.assertRaises(self.failureException, self.assertLess, 'bug', 'ant')
+ self.assertRaises(self.failureException, self.assertLess, 'ant', 'ant')
+ self.assertRaises(self.failureException, self.assertLessEqual, 'bug', 'ant')
+
+ # Try bytes
+ self.assertGreater(b'bug', b'ant')
+ self.assertGreaterEqual(b'bug', b'ant')
+ self.assertGreaterEqual(b'ant', b'ant')
+ self.assertLess(b'ant', b'bug')
+ self.assertLessEqual(b'ant', b'bug')
+ self.assertLessEqual(b'ant', b'ant')
+ self.assertRaises(self.failureException, self.assertGreater, b'ant', b'bug')
+ self.assertRaises(self.failureException, self.assertGreater, b'ant', b'ant')
+ self.assertRaises(self.failureException, self.assertGreaterEqual, b'ant',
+ b'bug')
+ self.assertRaises(self.failureException, self.assertLess, b'bug', b'ant')
+ self.assertRaises(self.failureException, self.assertLess, b'ant', b'ant')
+ self.assertRaises(self.failureException, self.assertLessEqual, b'bug', b'ant')
+
+ def testAssertMultiLineEqual(self):
+ sample_text = """\
+http://www.python.org/doc/2.3/lib/module-unittest.html
+test case
+ A test case is the smallest unit of testing. [...]
+"""
+ revised_sample_text = """\
+http://www.python.org/doc/2.4.1/lib/module-unittest.html
+test case
+ A test case is the smallest unit of testing. [...] You may provide your
+ own implementation that does not subclass from TestCase, of course.
+"""
+ sample_text_error = """
+- http://www.python.org/doc/2.3/lib/module-unittest.html
+? ^
++ http://www.python.org/doc/2.4.1/lib/module-unittest.html
+? ^^^
+ test case
+- A test case is the smallest unit of testing. [...]
++ A test case is the smallest unit of testing. [...] You may provide your
+? +++++++++++++++++++++
++ own implementation that does not subclass from TestCase, of course.
+"""
+
+ try:
+ self.assertMultiLineEqual(sample_text, revised_sample_text)
+ except self.failureException as e:
+ # no fair testing ourself with ourself, use assertEqual..
+ self.assertEqual(sample_text_error, str(e))
+
+ def testAssertIsNone(self):
+ self.assertIsNone(None)
+ self.assertRaises(self.failureException, self.assertIsNone, False)
+ self.assertIsNotNone('DjZoPloGears on Rails')
+ self.assertRaises(self.failureException, self.assertIsNotNone, None)
+
+ def testAssertRegexpMatches(self):
+ self.assertRegexpMatches('asdfabasdf', r'ab+')
+ self.assertRaises(self.failureException, self.assertRegexpMatches,
+ 'saaas', r'aaaa')
+
+ def testAssertRaisesRegexp(self):
+ class ExceptionMock(Exception):
+ pass
+
+ def Stub():
+ raise ExceptionMock('We expect')
+
+ self.assertRaisesRegexp(ExceptionMock, re.compile('expect$'), Stub)
+ self.assertRaisesRegexp(ExceptionMock, 'expect$', Stub)
+
+ def testAssertNotRaisesRegexp(self):
+ self.assertRaisesRegexp(
+ self.failureException, '^Exception not raised$',
+ self.assertRaisesRegexp, Exception, re.compile('x'),
+ lambda: None)
+ self.assertRaisesRegexp(
+ self.failureException, '^Exception not raised$',
+ self.assertRaisesRegexp, Exception, 'x',
+ lambda: None)
+
+ def testAssertRaisesRegexpMismatch(self):
+ def Stub():
+ raise Exception('Unexpected')
+
+ self.assertRaisesRegexp(
+ self.failureException,
+ r'"\^Expected\$" does not match "Unexpected"',
+ self.assertRaisesRegexp, Exception, '^Expected$',
+ Stub)
+ self.assertRaisesRegexp(
+ self.failureException,
+ r'"\^Expected\$" does not match "Unexpected"',
+ self.assertRaisesRegexp, Exception,
+ re.compile('^Expected$'), Stub)
+
+ def testSynonymAssertMethodNames(self):
+ """Test undocumented method name synonyms.
+
+ Please do not use these methods names in your own code.
+
+ This test confirms their continued existence and functionality
+ in order to avoid breaking existing code.
+ """
+ self.assertNotEquals(3, 5)
+ self.assertEquals(3, 3)
+ self.assertAlmostEquals(2.0, 2.0)
+ self.assertNotAlmostEquals(3.0, 5.0)
+ self.assert_(True)
+
+ def testPendingDeprecationMethodNames(self):
+ """Test fail* methods pending deprecation, they will warn in 3.2.
+
+ Do not use these methods. They will go away in 3.3.
+ """
+ self.failIfEqual(3, 5)
+ self.failUnlessEqual(3, 3)
+ self.failUnlessAlmostEqual(2.0, 2.0)
+ self.failIfAlmostEqual(3.0, 5.0)
+ self.failUnless(True)
+ self.failUnlessRaises(TypeError, lambda _: 3.14 + 'spam')
+ self.failIf(False)
+
+ def testDeepcopy(self):
+ # Issue: 5660
+ class TestableTest(TestCase):
+ def testNothing(self):
+ pass
+
+ test = TestableTest('testNothing')
+
+ # This shouldn't blow up
+ deepcopy(test)
+
class Test_TestSkipping(TestCase):
@@ -2396,20 +2767,20 @@ class Test_Assertions(TestCase):
def test_AlmostEqual(self):
self.failUnlessAlmostEqual(1.00000001, 1.0)
self.failIfAlmostEqual(1.0000001, 1.0)
- self.assertRaises(AssertionError,
+ self.assertRaises(self.failureException,
self.failUnlessAlmostEqual, 1.0000001, 1.0)
- self.assertRaises(AssertionError,
+ self.assertRaises(self.failureException,
self.failIfAlmostEqual, 1.00000001, 1.0)
self.failUnlessAlmostEqual(1.1, 1.0, places=0)
- self.assertRaises(AssertionError,
+ self.assertRaises(self.failureException,
self.failUnlessAlmostEqual, 1.1, 1.0, places=1)
self.failUnlessAlmostEqual(0, .1+.1j, places=0)
self.failIfAlmostEqual(0, .1+.1j, places=1)
- self.assertRaises(AssertionError,
+ self.assertRaises(self.failureException,
self.failUnlessAlmostEqual, 0, .1+.1j, places=1)
- self.assertRaises(AssertionError,
+ self.assertRaises(self.failureException,
self.failIfAlmostEqual, 0, .1+.1j, places=0)
def test_assertRaises(self):
@@ -2419,7 +2790,7 @@ class Test_Assertions(TestCase):
self.assertRaises(KeyError, _raise, KeyError("key"))
try:
self.assertRaises(KeyError, lambda: None)
- except AssertionError as e:
+ except self.failureException as e:
self.assert_("KeyError not raised" in str(e), str(e))
else:
self.fail("assertRaises() didn't fail")
@@ -2436,7 +2807,7 @@ class Test_Assertions(TestCase):
try:
with self.assertRaises(KeyError):
pass
- except AssertionError as e:
+ except self.failureException as e:
self.assert_("KeyError not raised" in str(e), str(e))
else:
self.fail("assertRaises() didn't fail")
@@ -2449,6 +2820,172 @@ class Test_Assertions(TestCase):
self.fail("assertRaises() didn't let exception pass through")
+class TestLongMessage(TestCase):
+ """Test that the individual asserts honour longMessage.
+ This actually tests all the message behaviour for
+ asserts that use longMessage."""
+
+ def setUp(self):
+ class TestableTestFalse(TestCase):
+ longMessage = False
+ failureException = self.failureException
+
+ def testTest(self):
+ pass
+
+ class TestableTestTrue(TestCase):
+ longMessage = True
+ failureException = self.failureException
+
+ def testTest(self):
+ pass
+
+ self.testableTrue = TestableTestTrue('testTest')
+ self.testableFalse = TestableTestFalse('testTest')
+
+ def testDefault(self):
+ self.assertFalse(TestCase.longMessage)
+
+ def test_formatMsg(self):
+ self.assertEquals(self.testableFalse._formatMessage(None, "foo"), "foo")
+ self.assertEquals(self.testableFalse._formatMessage("foo", "bar"), "foo")
+
+ self.assertEquals(self.testableTrue._formatMessage(None, "foo"), "foo")
+ self.assertEquals(self.testableTrue._formatMessage("foo", "bar"), "bar : foo")
+
+ def assertMessages(self, methodName, args, errors):
+ def getMethod(i):
+ useTestableFalse = i < 2
+ if useTestableFalse:
+ test = self.testableFalse
+ else:
+ test = self.testableTrue
+ return getattr(test, methodName)
+
+ for i, expected_regexp in enumerate(errors):
+ testMethod = getMethod(i)
+ kwargs = {}
+ withMsg = i % 2
+ if withMsg:
+ kwargs = {"msg": "oops"}
+
+ with self.assertRaisesRegexp(self.failureException,
+ expected_regexp=expected_regexp):
+ testMethod(*args, **kwargs)
+
+ def testAssertTrue(self):
+ self.assertMessages('assertTrue', (False,),
+ ["^False is not True$", "^oops$", "^False is not True$",
+ "^False is not True : oops$"])
+
+ def testAssertFalse(self):
+ self.assertMessages('assertFalse', (True,),
+ ["^True is not False$", "^oops$", "^True is not False$",
+ "^True is not False : oops$"])
+
+ def testNotEqual(self):
+ self.assertMessages('assertNotEqual', (1, 1),
+ ["^1 == 1$", "^oops$", "^1 == 1$",
+ "^1 == 1 : oops$"])
+
+ def testAlmostEqual(self):
+ self.assertMessages('assertAlmostEqual', (1, 2),
+ ["^1 != 2 within 7 places$", "^oops$",
+ "^1 != 2 within 7 places$", "^1 != 2 within 7 places : oops$"])
+
+ def testNotAlmostEqual(self):
+ self.assertMessages('assertNotAlmostEqual', (1, 1),
+ ["^1 == 1 within 7 places$", "^oops$",
+ "^1 == 1 within 7 places$", "^1 == 1 within 7 places : oops$"])
+
+ def test_baseAssertEqual(self):
+ self.assertMessages('_baseAssertEqual', (1, 2),
+ ["^1 != 2$", "^oops$", "^1 != 2$", "^1 != 2 : oops$"])
+
+ def testAssertSequenceEqual(self):
+ # Error messages are multiline so not testing on full message
+ # assertTupleEqual and assertListEqual delegate to this method
+ self.assertMessages('assertSequenceEqual', ([], [None]),
+ ["\+ \[None\]$", "^oops$", r"\+ \[None\]$",
+ r"\+ \[None\] : oops$"])
+
+ def testAssertSetEqual(self):
+ self.assertMessages('assertSetEqual', (set(), set([None])),
+ ["None$", "^oops$", "None$",
+ "None : oops$"])
+
+ def testAssertIn(self):
+ self.assertMessages('assertIn', (None, []),
+ ['^None not found in \[\]$', "^oops$",
+ '^None not found in \[\]$',
+ '^None not found in \[\] : oops$'])
+
+ def testAssertNotIn(self):
+ self.assertMessages('assertNotIn', (None, [None]),
+ ['^None unexpectedly found in \[None\]$', "^oops$",
+ '^None unexpectedly found in \[None\]$',
+ '^None unexpectedly found in \[None\] : oops$'])
+
+ def testAssertDictEqual(self):
+ self.assertMessages('assertDictEqual', ({}, {'key': 'value'}),
+ [r"\+ \{'key': 'value'\}$", "^oops$",
+ "\+ \{'key': 'value'\}$",
+ "\+ \{'key': 'value'\} : oops$"])
+
+ def testAssertDictContainsSubset(self):
+ self.assertMessages('assertDictContainsSubset', ({'key': 'value'}, {}),
+ ["^Missing: 'key'$", "^oops$",
+ "^Missing: 'key'$",
+ "^Missing: 'key' : oops$"])
+
+ def testAssertSameElements(self):
+ self.assertMessages('assertSameElements', ([], [None]),
+ [r"\[None\]$", "^oops$",
+ r"\[None\]$",
+ r"\[None\] : oops$"])
+
+ def testAssertMultiLineEqual(self):
+ self.assertMessages('assertMultiLineEqual', ("", "foo"),
+ [r"\+ foo$", "^oops$",
+ r"\+ foo$",
+ r"\+ foo : oops$"])
+
+ def testAssertLess(self):
+ self.assertMessages('assertLess', (2, 1),
+ ["^2 not less than 1$", "^oops$",
+ "^2 not less than 1$", "^2 not less than 1 : oops$"])
+
+ def testAssertLessEqual(self):
+ self.assertMessages('assertLessEqual', (2, 1),
+ ["^2 not less than or equal to 1$", "^oops$",
+ "^2 not less than or equal to 1$",
+ "^2 not less than or equal to 1 : oops$"])
+
+ def testAssertGreater(self):
+ self.assertMessages('assertGreater', (1, 2),
+ ["^1 not greater than 2$", "^oops$",
+ "^1 not greater than 2$",
+ "^1 not greater than 2 : oops$"])
+
+ def testAssertGreaterEqual(self):
+ self.assertMessages('assertGreaterEqual', (1, 2),
+ ["^1 not greater than or equal to 2$", "^oops$",
+ "^1 not greater than or equal to 2$",
+ "^1 not greater than or equal to 2 : oops$"])
+
+ def testAssertIsNone(self):
+ self.assertMessages('assertIsNone', ('not None',),
+ ["^'not None' is not None$", "^oops$",
+ "^'not None' is not None$",
+ "^'not None' is not None : oops$"])
+
+ def testAssertIsNotNone(self):
+ self.assertMessages('assertIsNotNone', (None,),
+ ["^unexpectedly None$", "^oops$",
+ "^unexpectedly None$",
+ "^unexpectedly None : oops$"])
+
+
######################################################################
## Main
######################################################################
@@ -2456,7 +2993,7 @@ class Test_Assertions(TestCase):
def test_main():
support.run_unittest(Test_TestCase, Test_TestLoader,
Test_TestSuite, Test_TestResult, Test_FunctionTestCase,
- Test_TestSkipping, Test_Assertions)
+ Test_TestSkipping, Test_Assertions, TestLongMessage)
if __name__ == "__main__":
test_main()
diff --git a/Lib/unittest.py b/Lib/unittest.py
index ade806c..16a8663 100644
--- a/Lib/unittest.py
+++ b/Lib/unittest.py
@@ -14,11 +14,11 @@ Simple usage:
class IntegerArithmenticTestCase(unittest.TestCase):
def testAdd(self): ## test method names begin 'test*'
- self.assertEquals((1 + 2), 3)
- self.assertEquals(0 + 1, 1)
+ self.assertEqual((1 + 2), 3)
+ self.assertEqual(0 + 1, 1)
def testMultiply(self):
- self.assertEquals((0 * 10), 0)
- self.assertEquals((5 * 8), 40)
+ self.assertEqual((0 * 10), 0)
+ self.assertEqual((5 * 8), 40)
if __name__ == '__main__':
unittest.main()
@@ -45,12 +45,16 @@ AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
'''
-import time
+import difflib
+import functools
+import os
+import pprint
+import re
import sys
+import time
import traceback
-import os
import types
-import functools
+import warnings
##############################################################################
# Exported classes and functions
@@ -143,7 +147,6 @@ def expectedFailure(func):
raise _UnexpectedSuccess
return wrapper
-
__unittest = 1
class TestResult(object):
@@ -239,10 +242,12 @@ class TestResult(object):
len(self.failures))
-class AssertRaisesContext(object):
+class _AssertRaisesContext(object):
+ """A context manager used to implement TestCase.assertRaises* methods."""
- def __init__(self, expected, test_case, callable_obj=None):
+ def __init__(self, expected, test_case, callable_obj=None,
+ expected_regexp=None):
self.expected = expected
self.failureException = test_case.failureException
if callable_obj is not None:
@@ -252,6 +257,7 @@ class AssertRaisesContext(object):
self.obj_name = str(callable_obj)
else:
self.obj_name = None
+ self.expected_regex = expected_regexp
def __enter__(self):
pass
@@ -268,10 +274,30 @@ class AssertRaisesContext(object):
else:
raise self.failureException("{0} not raised"
.format(exc_name))
- if issubclass(exc_type, self.expected):
+ if not issubclass(exc_type, self.expected):
+ # let unexpected exceptions pass through
+ return False
+ if self.expected_regex is None:
return True
- # Let unexpected exceptions skip through
- return False
+
+ expected_regexp = self.expected_regex
+ if isinstance(expected_regexp, (bytes, str)):
+ expected_regexp = re.compile(expected_regexp)
+ if not expected_regexp.search(str(exc_value)):
+ raise self.failureException('"%s" does not match "%s"' %
+ (expected_regexp.pattern, str(exc_value)))
+ return True
+
+
+class _AssertWrapper(object):
+ """Wrap entries in the _type_equality_funcs registry to make them deep
+ copyable."""
+
+ def __init__(self, function):
+ self.function = function
+
+ def __deepcopy__(self, memo):
+ memo[id(self)] = self
class TestCase(object):
@@ -302,6 +328,13 @@ class TestCase(object):
failureException = AssertionError
+ # This attribute determines whether long messages (including repr of
+ # objects used in assert methods) will be printed on failure in *addition*
+ # to any explicit message passed.
+
+ longMessage = False
+
+
def __init__(self, methodName='runTest'):
"""Create an instance of the class that will use the named test
method when executed. Raises a ValueError if the instance does
@@ -315,6 +348,31 @@ class TestCase(object):
(self.__class__, methodName))
self._testMethodDoc = testMethod.__doc__
+ # Map types to custom assertEqual functions that will compare
+ # instances of said type in more detail to generate a more useful
+ # error message.
+ self._type_equality_funcs = {}
+ self.addTypeEqualityFunc(dict, self.assertDictEqual)
+ self.addTypeEqualityFunc(list, self.assertListEqual)
+ self.addTypeEqualityFunc(tuple, self.assertTupleEqual)
+ self.addTypeEqualityFunc(set, self.assertSetEqual)
+ self.addTypeEqualityFunc(frozenset, self.assertSetEqual)
+
+ def addTypeEqualityFunc(self, typeobj, function):
+ """Add a type specific assertEqual style function to compare a type.
+
+ This method is for use by TestCase subclasses that need to register
+ their own type equality functions to provide nicer error messages.
+
+ Args:
+ typeobj: The data type to call this function on when both values
+ are of the same type in assertEqual().
+ function: The callable taking two arguments and an optional
+ msg= argument that raises self.failureException with a
+ useful error message when the two arguments are not equal.
+ """
+ self._type_equality_funcs[typeobj] = _AssertWrapper(function)
+
def setUp(self):
"Hook method for setting up the test fixture before exercising it."
pass
@@ -330,14 +388,22 @@ class TestCase(object):
return TestResult()
def shortDescription(self):
- """Returns a one-line description of the test, or None if no
- description has been provided.
+ """Returns both the test method name and first line of its docstring.
- The default implementation of this method returns the first line of
- the specified test method's docstring.
+ If no docstring is given, only returns the method name.
+
+ This method overrides unittest.TestCase.shortDescription(), which
+ only returns the first line of the docstring, obscuring the name
+ of the test upon failure.
"""
- doc = self._testMethodDoc
- return doc and doc.split("\n")[0].strip() or None
+ desc = str(self)
+ doc_first_line = None
+
+ if self._testMethodDoc:
+ doc_first_line = self._testMethodDoc.split("\n")[0].strip()
+ if doc_first_line:
+ desc = '\n'.join((desc, doc_first_line))
+ return desc
def id(self):
return "%s.%s" % (_strclass(self.__class__), self._testMethodName)
@@ -419,17 +485,36 @@ class TestCase(object):
"""Fail immediately, with the given message."""
raise self.failureException(msg)
- def failIf(self, expr, msg=None):
+ def assertFalse(self, expr, msg=None):
"Fail the test if the expression is true."
if expr:
+ msg = self._formatMessage(msg, "%r is not False" % expr)
raise self.failureException(msg)
- def failUnless(self, expr, msg=None):
+ def assertTrue(self, expr, msg=None):
"""Fail the test unless the expression is true."""
if not expr:
+ msg = self._formatMessage(msg, "%r is not True" % expr)
raise self.failureException(msg)
- def failUnlessRaises(self, excClass, callableObj=None, *args, **kwargs):
+ 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 message
+ """
+ if not self.longMessage:
+ return msg or standardMsg
+ if msg is None:
+ return standardMsg
+ return standardMsg + ' : ' + msg
+
+
+ def assertRaises(self, excClass, callableObj=None, *args, **kwargs):
"""Fail unless an exception of class excClass is thrown
by callableObj when invoked with arguments args and keyword
arguments kwargs. If a different type of exception is
@@ -440,30 +525,62 @@ class TestCase(object):
If called with callableObj omitted or None, will return a
context object used like this::
- with self.failUnlessRaises(some_error_class):
+ with self.assertRaises(some_error_class):
do_something()
"""
- context = AssertRaisesContext(excClass, self, callableObj)
+ context = _AssertRaisesContext(excClass, self, callableObj)
if callableObj is None:
return context
with context:
callableObj(*args, **kwargs)
- def failUnlessEqual(self, first, second, msg=None):
+ def _getAssertEqualityFunc(self, first, second):
+ """Get a detailed comparison function for the types of the two args.
+
+ Returns: A callable accepting (first, second, msg=None) that will
+ raise a failure exception if first != second with a useful human
+ readable error message for those types.
+ """
+ #
+ # NOTE(gregory.p.smith): I considered isinstance(first, type(second))
+ # and vice versa. I opted for the conservative approach in case
+ # subclasses are not intended to be compared in detail to their super
+ # class instances using a type equality func. This means testing
+ # subtypes won't automagically use the detailed comparison. Callers
+ # should use their type specific assertSpamEqual method to compare
+ # subclasses if the detailed comparison is desired and appropriate.
+ # See the discussion in http://bugs.python.org/issue2578.
+ #
+ if type(first) is type(second):
+ asserter = self._type_equality_funcs.get(type(first))
+ if asserter is not None:
+ return asserter.function
+
+ return self._baseAssertEqual
+
+ def _baseAssertEqual(self, first, second, msg=None):
+ """The default assertEqual implementation, not type specific."""
+ if not first == second:
+ standardMsg = '%r != %r' % (first, second)
+ msg = self._formatMessage(msg, standardMsg)
+ raise self.failureException(msg)
+
+ def assertEqual(self, first, second, msg=None):
"""Fail if the two objects are unequal as determined by the '=='
operator.
"""
- if not first == second:
- raise self.failureException(msg or '%r != %r' % (first, second))
+ assertion_func = self._getAssertEqualityFunc(first, second)
+ assertion_func(first, second, msg=msg)
- def failIfEqual(self, first, second, msg=None):
+ def assertNotEqual(self, first, second, msg=None):
"""Fail if the two objects are equal as determined by the '=='
operator.
"""
- if first == second:
- raise self.failureException(msg or '%r == %r' % (first, second))
+ if not first != second:
+ msg = self._formatMessage(msg, '%r == %r' % (first, second))
+ raise self.failureException(msg)
- def failUnlessAlmostEqual(self, first, second, *, places=7, msg=None):
+ def assertAlmostEqual(self, first, second, *, places=7, msg=None):
"""Fail if the two objects are unequal as determined by their
difference rounded to the given number of decimal places
(default 7) and comparing to zero.
@@ -472,10 +589,11 @@ class TestCase(object):
as significant digits (measured from the most signficant digit).
"""
if round(abs(second-first), places) != 0:
- raise self.failureException(
- msg or '%r != %r within %r places' % (first, second, places))
+ standardMsg = '%r != %r within %r places' % (first, second, places)
+ msg = self._formatMessage(msg, standardMsg)
+ raise self.failureException(msg)
- def failIfAlmostEqual(self, first, second, *, places=7, msg=None):
+ def assertNotAlmostEqual(self, first, second, *, places=7, msg=None):
"""Fail if the two objects are equal as determined by their
difference rounded to the given number of decimal places
(default 7) and comparing to zero.
@@ -484,25 +602,388 @@ class TestCase(object):
as significant digits (measured from the most signficant digit).
"""
if round(abs(second-first), places) == 0:
- raise self.failureException(
- msg or '%r == %r within %r places' % (first, second, places))
+ standardMsg = '%r == %r within %r places' % (first, second, places)
+ msg = self._formatMessage(msg, standardMsg)
+ raise self.failureException(msg)
# Synonyms for assertion methods
- assertEqual = assertEquals = failUnlessEqual
+ # The plurals are undocumented. Keep them that way to discourage use.
+ # Do not add more. Do not remove.
+ # Going through a deprecation cycle on these would annoy many people.
+ assertEquals = assertEqual
+ assertNotEquals = assertNotEqual
+ assertAlmostEquals = assertAlmostEqual
+ assertNotAlmostEquals = assertNotAlmostEqual
+ assert_ = assertTrue
+
+ # These fail* assertion method names are pending deprecation and will
+ # be a DeprecationWarning in 3.2; http://bugs.python.org/issue2578
+ def _deprecate(original_func):
+ def deprecated_func(*args, **kwargs):
+ warnings.warn(
+ 'Please use {0} instead.'.format(original_func.__name__),
+ PendingDeprecationWarning, 2)
+ return original_func(*args, **kwargs)
+ return deprecated_func
+
+ failUnlessEqual = _deprecate(assertEqual)
+ failIfEqual = _deprecate(assertNotEqual)
+ failUnlessAlmostEqual = _deprecate(assertAlmostEqual)
+ failIfAlmostEqual = _deprecate(assertNotAlmostEqual)
+ failUnless = _deprecate(assertTrue)
+ failUnlessRaises = _deprecate(assertRaises)
+ failIf = _deprecate(assertFalse)
+
+ def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None):
+ """An equality assertion for ordered sequences (like lists and tuples).
+
+ For the purposes of this function, a valid orderd sequence type is one
+ which can be indexed, has a length, and has an equality operator.
+
+ Args:
+ seq1: The first sequence to compare.
+ seq2: The second sequence to compare.
+ seq_type: The expected datatype of the sequences, or None if no
+ datatype should be enforced.
+ msg: Optional message to use on failure instead of a list of
+ differences.
+ """
+ if seq_type != None:
+ seq_type_name = seq_type.__name__
+ if not isinstance(seq1, seq_type):
+ raise self.failureException('First sequence is not a %s: %r'
+ % (seq_type_name, seq1))
+ if not isinstance(seq2, seq_type):
+ raise self.failureException('Second sequence is not a %s: %r'
+ % (seq_type_name, seq2))
+ else:
+ seq_type_name = "sequence"
+
+ differing = None
+ try:
+ len1 = len(seq1)
+ except (TypeError, NotImplementedError):
+ differing = 'First %s has no length. Non-sequence?' % (
+ seq_type_name)
+
+ if differing is None:
+ try:
+ len2 = len(seq2)
+ except (TypeError, NotImplementedError):
+ differing = 'Second %s has no length. Non-sequence?' % (
+ seq_type_name)
+
+ if differing is None:
+ if seq1 == seq2:
+ return
+
+ for i in range(min(len1, len2)):
+ try:
+ item1 = seq1[i]
+ except (TypeError, IndexError, NotImplementedError):
+ differing = ('Unable to index element %d of first %s\n' %
+ (i, seq_type_name))
+ break
+
+ try:
+ item2 = seq2[i]
+ except (TypeError, IndexError, NotImplementedError):
+ differing = ('Unable to index element %d of second %s\n' %
+ (i, seq_type_name))
+ break
+
+ if item1 != item2:
+ differing = ('First differing element %d:\n%s\n%s\n' %
+ (i, item1, item2))
+ break
+ else:
+ if (len1 == len2 and seq_type is None and
+ type(seq1) != type(seq2)):
+ # The sequences are the same, but have differing types.
+ return
+ # A catch-all message for handling arbitrary user-defined
+ # sequences.
+ differing = '%ss differ:\n' % seq_type_name.capitalize()
+ if len1 > len2:
+ differing = ('First %s contains %d additional '
+ 'elements.\n' % (seq_type_name, len1 - len2))
+ try:
+ differing += ('First extra element %d:\n%s\n' %
+ (len2, seq1[len2]))
+ except (TypeError, IndexError, NotImplementedError):
+ differing += ('Unable to index element %d '
+ 'of first %s\n' % (len2, seq_type_name))
+ elif len1 < len2:
+ differing = ('Second %s contains %d additional '
+ 'elements.\n' % (seq_type_name, len2 - len1))
+ try:
+ differing += ('First extra element %d:\n%s\n' %
+ (len1, seq2[len1]))
+ except (TypeError, IndexError, NotImplementedError):
+ differing += ('Unable to index element %d '
+ 'of second %s\n' % (len1, seq_type_name))
+ standardMsg = differing + '\n'.join(difflib.ndiff(pprint.pformat(seq1).splitlines(),
+ pprint.pformat(seq2).splitlines()))
+ msg = self._formatMessage(msg, standardMsg)
+ self.fail(msg)
+
+ def assertListEqual(self, list1, list2, msg=None):
+ """A list-specific equality assertion.
+
+ Args:
+ list1: The first list to compare.
+ list2: The second list to compare.
+ msg: Optional message to use on failure instead of a list of
+ differences.
+
+ """
+ self.assertSequenceEqual(list1, list2, msg, seq_type=list)
- assertNotEqual = assertNotEquals = failIfEqual
+ def assertTupleEqual(self, tuple1, tuple2, msg=None):
+ """A tuple-specific equality assertion.
- assertAlmostEqual = assertAlmostEquals = failUnlessAlmostEqual
+ Args:
+ tuple1: The first tuple to compare.
+ tuple2: The second tuple to compare.
+ msg: Optional message to use on failure instead of a list of
+ differences.
+ """
+ self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple)
- assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual
+ def assertSetEqual(self, set1, set2, msg=None):
+ """A set-specific equality assertion.
- assertRaises = failUnlessRaises
+ Args:
+ set1: The first set to compare.
+ set2: The second set to compare.
+ msg: Optional message to use on failure instead of a list of
+ differences.
- assert_ = assertTrue = failUnless
+ For more general containership equality, assertSameElements will work
+ with things other than sets. This uses ducktyping to support
+ different types of sets, and is optimized for sets specifically
+ (parameters must support a difference method).
+ """
+ try:
+ difference1 = set1.difference(set2)
+ except TypeError as e:
+ self.fail('invalid type when attempting set difference: %s' % e)
+ except AttributeError as e:
+ self.fail('first argument does not support set difference: %s' % e)
- assertFalse = failIf
+ try:
+ difference2 = set2.difference(set1)
+ except TypeError as e:
+ self.fail('invalid type when attempting set difference: %s' % e)
+ except AttributeError as e:
+ self.fail('second argument does not support set difference: %s' % e)
+
+ if not (difference1 or difference2):
+ return
+
+ lines = []
+ if difference1:
+ lines.append('Items in the first set but not the second:')
+ for item in difference1:
+ lines.append(repr(item))
+ if difference2:
+ lines.append('Items in the second set but not the first:')
+ for item in difference2:
+ lines.append(repr(item))
+
+ standardMsg = '\n'.join(lines)
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertIn(self, member, container, msg=None):
+ """Just like self.assertTrue(a in b), but with a nicer default message."""
+ if member not in container:
+ standardMsg = '%r not found in %r' % (member, container)
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertNotIn(self, member, container, msg=None):
+ """Just like self.assertTrue(a not in b), but with a nicer default message."""
+ if member in container:
+ standardMsg = '%r unexpectedly found in %r' % (member, container)
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertDictEqual(self, d1, d2, msg=None):
+ self.assert_(isinstance(d1, dict), 'First argument is not a dictionary')
+ self.assert_(isinstance(d2, dict), 'Second argument is not a dictionary')
+
+ if d1 != d2:
+ standardMsg = ('\n' + '\n'.join(difflib.ndiff(
+ pprint.pformat(d1).splitlines(),
+ pprint.pformat(d2).splitlines())))
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertDictContainsSubset(self, expected, actual, msg=None):
+ """Checks whether actual is a superset of expected."""
+ missing = []
+ mismatched = []
+ for key, value in expected.items():
+ if key not in actual:
+ missing.append(key)
+ elif value != actual[key]:
+ mismatched.append('%s, expected: %s, actual: %s' % (key, value, actual[key]))
+
+ if not (missing or mismatched):
+ return
+
+ standardMsg = ''
+ if missing:
+ standardMsg = 'Missing: %r' % ','.join(missing)
+ if mismatched:
+ if standardMsg:
+ standardMsg += '; '
+ standardMsg += 'Mismatched values: %s' % ','.join(mismatched)
+
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertSameElements(self, expected_seq, actual_seq, msg=None):
+ """An unordered sequence specific comparison.
+
+ Raises with an error message listing which elements of expected_seq
+ are missing from actual_seq and vice versa if any.
+ """
+ try:
+ expected = set(expected_seq)
+ actual = set(actual_seq)
+ missing = list(expected.difference(actual))
+ unexpected = list(actual.difference(expected))
+ missing.sort()
+ unexpected.sort()
+ except TypeError:
+ # Fall back to slower list-compare if any of the objects are
+ # not hashable.
+ expected = list(expected_seq)
+ actual = list(actual_seq)
+ expected.sort()
+ actual.sort()
+ missing, unexpected = _SortedListDifference(expected, actual)
+ errors = []
+ if missing:
+ errors.append('Expected, but missing:\n %r' % missing)
+ if unexpected:
+ errors.append('Unexpected, but present:\n %r' % unexpected)
+ if errors:
+ standardMsg = '\n'.join(errors)
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertMultiLineEqual(self, first, second, msg=None):
+ """Assert that two multi-line strings are equal."""
+ self.assert_(isinstance(first, str), (
+ 'First argument is not a string'))
+ self.assert_(isinstance(second, str), (
+ 'Second argument is not a string'))
+
+ if first != second:
+ standardMsg = '\n' + ''.join(difflib.ndiff(first.splitlines(True), second.splitlines(True)))
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertLess(self, a, b, msg=None):
+ """Just like self.assertTrue(a < b), but with a nicer default message."""
+ if not a < b:
+ standardMsg = '%r not less than %r' % (a, b)
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertLessEqual(self, a, b, msg=None):
+ """Just like self.assertTrue(a <= b), but with a nicer default message."""
+ if not a <= b:
+ standardMsg = '%r not less than or equal to %r' % (a, b)
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertGreater(self, a, b, msg=None):
+ """Just like self.assertTrue(a > b), but with a nicer default message."""
+ if not a > b:
+ standardMsg = '%r not greater than %r' % (a, b)
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertGreaterEqual(self, a, b, msg=None):
+ """Just like self.assertTrue(a >= b), but with a nicer default message."""
+ if not a >= b:
+ standardMsg = '%r not greater than or equal to %r' % (a, b)
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertIsNone(self, obj, msg=None):
+ """Same as self.assertTrue(obj is None), with a nicer default message."""
+ if obj is not None:
+ standardMsg = '%r is not None' % obj
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertIsNotNone(self, obj, msg=None):
+ """Included for symmetry with assertIsNone."""
+ if obj is None:
+ standardMsg = 'unexpectedly None'
+ self.fail(self._formatMessage(msg, standardMsg))
+
+ def assertRaisesRegexp(self, expected_exception, expected_regexp,
+ callable_obj=None, *args, **kwargs):
+ """Asserts that the message in a raised exception matches a regexp.
+
+ Args:
+ expected_exception: Exception class expected to be raised.
+ expected_regexp: Regexp (re pattern object or string) expected
+ to be found in error message.
+ callable_obj: Function to be called.
+ args: Extra args.
+ kwargs: Extra kwargs.
+ """
+ context = _AssertRaisesContext(expected_exception, self, callable_obj,
+ expected_regexp)
+ if callable_obj is None:
+ return context
+ with context:
+ callable_obj(*args, **kwargs)
+
+ def assertRegexpMatches(self, text, expected_regex, msg=None):
+ if isinstance(expected_regex, (str, bytes)):
+ expected_regex = re.compile(expected_regex)
+ if not expected_regex.search(text):
+ msg = msg or "Regexp didn't match"
+ msg = '%s: %r not found in %r' % (msg, expected_regex.pattern, text)
+ raise self.failureException(msg)
+
+
+def _SortedListDifference(expected, actual):
+ """Finds elements in only one or the other of two, sorted input lists.
+ Returns a two-element tuple of lists. The first list contains those
+ elements in the "expected" list but not in the "actual" list, and the
+ second contains those elements in the "actual" list but not in the
+ "expected" list. Duplicate elements in either input list are ignored.
+ """
+ i = j = 0
+ missing = []
+ unexpected = []
+ while True:
+ try:
+ e = expected[i]
+ a = actual[j]
+ if e < a:
+ missing.append(e)
+ i += 1
+ while expected[i] == e:
+ i += 1
+ elif e > a:
+ unexpected.append(a)
+ j += 1
+ while actual[j] == a:
+ j += 1
+ else:
+ i += 1
+ try:
+ while expected[i] == e:
+ i += 1
+ finally:
+ j += 1
+ while actual[j] == a:
+ j += 1
+ except IndexError:
+ missing.extend(expected[i:])
+ unexpected.extend(actual[j:])
+ break
+ return missing, unexpected
class TestSuite(object):
@@ -611,52 +1092,52 @@ class FunctionTestCase(TestCase):
def __init__(self, testFunc, setUp=None, tearDown=None, description=None):
super(FunctionTestCase, self).__init__()
- self.__setUpFunc = setUp
- self.__tearDownFunc = tearDown
- self.__testFunc = testFunc
- self.__description = description
+ self._setUpFunc = setUp
+ self._tearDownFunc = tearDown
+ self._testFunc = testFunc
+ self._description = description
def setUp(self):
- if self.__setUpFunc is not None:
- self.__setUpFunc()
+ if self._setUpFunc is not None:
+ self._setUpFunc()
def tearDown(self):
- if self.__tearDownFunc is not None:
- self.__tearDownFunc()
+ if self._tearDownFunc is not None:
+ self._tearDownFunc()
def runTest(self):
- self.__testFunc()
+ self._testFunc()
def id(self):
- return self.__testFunc.__name__
+ return self._testFunc.__name__
def __eq__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
- return self.__setUpFunc == other.__setUpFunc and \
- self.__tearDownFunc == other.__tearDownFunc and \
- self.__testFunc == other.__testFunc and \
- self.__description == other.__description
+ return self._setUpFunc == other._setUpFunc and \
+ self._tearDownFunc == other._tearDownFunc and \
+ self._testFunc == other._testFunc and \
+ self._description == other._description
def __ne__(self, other):
return not self == other
def __hash__(self):
- return hash((type(self), self.__setUpFunc, self.__tearDownFunc,
- self.__testFunc, self.__description))
+ return hash((type(self), self._setUpFunc, self._tearDownFunc,
+ self._testFunc, self._description))
def __str__(self):
return "%s (%s)" % (_strclass(self.__class__),
self.__testFunc.__name__)
def __repr__(self):
- return "<%s testFunc=%s>" % (_strclass(self.__class__),
- self.__testFunc)
+ return "<%s testFunc=%s>" % (_strclass(self.__class__), self._testFunc)
def shortDescription(self):
- if self.__description is not None: return self.__description
- doc = self.__testFunc.__doc__
+ if self._description is not None:
+ return self._description
+ doc = self._testFunc.__doc__
return doc and doc.split("\n")[0].strip() or None