Index: Misc/NEWS =================================================================== --- Misc/NEWS (revision 71017) +++ Misc/NEWS (working copy) @@ -1993,6 +1993,9 @@ Library ------- +- unittest.assertNotEqual now uses the inequality operator (!=) instead + of the equality operator. + - Issue #2315: logging.handlers: TimedRotatingFileHandler now accounts for daylight savings time in calculating the next rollover. Index: Lib/unittest.py =================================================================== --- Lib/unittest.py (revision 71017) +++ Lib/unittest.py (working copy) @@ -275,7 +275,7 @@ raise self.failureException( "{0} not raised".format(exc_name)) if not issubclass(exc_type, self.expected): - # let unexpexted exceptions pass through + # let unexpected exceptions pass through return False if self.expected_regex is None: return True @@ -317,6 +317,13 @@ # exception will be deemed to have 'failed' rather than 'errored' 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 @@ -471,13 +478,24 @@ 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 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 _formatMessage(self, msg, standardMsg): + """Honour the longMessage attribute when generating failure messages.""" + 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 @@ -523,7 +541,9 @@ def _baseAssertEqual(self, first, second, msg=None): """The default assertEqual implementation, not type specific.""" if not first == second: - raise self.failureException(msg or '%r != %r' % (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 '==' @@ -536,8 +556,9 @@ """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 assertAlmostEqual(self, first, second, places=7, msg=None): """Fail if the two objects are unequal as determined by their @@ -548,8 +569,9 @@ 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 assertNotAlmostEqual(self, first, second, places=7, msg=None): """Fail if the two objects are equal as determined by their @@ -560,8 +582,9 @@ 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 @@ -680,11 +703,11 @@ except (TypeError, IndexError, NotImplementedError): differing += ('Unable to index element %d ' 'of second %s\n' % (len1, seq_type_name)) - if not msg: - msg = '\n'.join(difflib.ndiff(pprint.pformat(seq1).splitlines(), - pprint.pformat(seq2).splitlines())) - self.fail(differing + msg) - + 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. @@ -739,9 +762,6 @@ if not (difference1 or difference2): return - if msg is not None: - self.fail(msg) - lines = [] if difference1: lines.append('Items in the first set but not the second:') @@ -751,28 +771,31 @@ lines.append('Items in the second set but not the first:') for item in difference2: lines.append(repr(item)) - self.fail('\n'.join(lines)) + + standardMsg = '\n'.join(lines) + self.fail(self._formatMessage(msg, standardMsg)) - def assertIn(self, a, b, msg=None): + def assertIn(self, member, container, msg=None): """Just like self.assert_(a in b), but with a nicer default message.""" - if msg is None: - msg = '"%s" not found in "%s"' % (a, b) - self.assert_(a in b, msg) - - def assertNotIn(self, a, b, msg=None): + 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.assert_(a not in b), but with a nicer default message.""" - if msg is None: - msg = '"%s" unexpectedly found in "%s"' % (a, b) - self.assert_(a not in b, msg) + 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: - self.fail(msg or ('\n' + '\n'.join(difflib.ndiff( - pprint.pformat(d1).splitlines(), - pprint.pformat(d2).splitlines())))) + 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.""" @@ -782,23 +805,20 @@ if key not in actual: missing.append(key) elif value != actual[key]: - mismatched.append('%s, expected: %s, actual: %s' % (key, value, - actual[key])) + mismatched.append('%s, expected: %s, actual: %s' % (key, value, actual[key])) if not (missing or mismatched): return - missing_msg = mismatched_msg = '' + standardMsg = '' if missing: - missing_msg = 'Missing: %s' % ','.join(missing) + standardMsg = 'Missing: %r' % ','.join(missing) if mismatched: - mismatched_msg = 'Mismatched values: %s' % ','.join(mismatched) + if standardMsg: + standardMsg += '; ' + standardMsg += 'Mismatched values: %s' % ','.join(mismatched) - if msg: - msg = '%s: %s; %s' % (msg, missing_msg, mismatched_msg) - else: - msg = '%s; %s' % (missing_msg, mismatched_msg) - self.fail(msg) + self.fail(self._formatMessage(msg, standardMsg)) def assertSameElements(self, expected_seq, actual_seq, msg=None): """An unordered sequence specific comparison. @@ -823,57 +843,59 @@ missing, unexpected = _SortedListDifference(expected, actual) errors = [] if missing: - errors.append('Expected, but missing:\n %r\n' % missing) + errors.append('Expected, but missing:\n %r' % missing) if unexpected: - errors.append('Unexpected, but present:\n %r\n' % unexpected) + errors.append('Unexpected, but present:\n %r' % unexpected) if errors: - self.fail(msg or ''.join(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, types.StringTypes), ( + self.assert_(isinstance(first, basestring), ( 'First argument is not a string')) - self.assert_(isinstance(second, types.StringTypes), ( + self.assert_(isinstance(second, basestring), ( 'Second argument is not a string')) if first != second: - raise self.failureException( - msg or '\n' + ''.join(difflib.ndiff(first.splitlines(True), - second.splitlines(True)))) + 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.assert_(a < b), but with a nicer default message.""" - if msg is None: - msg = '"%r" unexpectedly not less than "%r"' % (a, b) - self.assert_(a < b, msg) + """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.assert_(a <= b), but with a nicer default message.""" - if msg is None: - msg = '"%r" unexpectedly not less than or equal to "%r"' % (a, b) - self.assert_(a <= b, msg) + """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.assert_(a > b), but with a nicer default message.""" - if msg is None: - msg = '"%r" unexpectedly not greater than "%r"' % (a, b) - self.assert_(a > b, msg) + """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.assert_(a >= b), but with a nicer default message.""" - if msg is None: - msg = '"%r" unexpectedly not greater than or equal to "%r"' % (a, b) - self.assert_(a >= b, msg) + """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.assert_(obj is None), with a nicer default message.""" - if msg is None: - msg = '"%s" unexpectedly not None' % obj - self.assert_(obj is None, msg) + """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='unexpectedly None'): + def assertIsNotNone(self, obj, msg=None): """Included for symmetry with assertIsNone.""" - self.assert_(obj is not None, msg) + 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): Index: Lib/test/test_unittest.py =================================================================== --- Lib/test/test_unittest.py (revision 71017) +++ Lib/test/test_unittest.py (working copy) @@ -54,6 +54,8 @@ 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: @@ -67,6 +69,8 @@ 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: @@ -2835,6 +2839,172 @@ 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 ###################################################################### @@ -2842,7 +3012,7 @@ def test_main(): test_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()