diff options
| -rw-r--r-- | lib/testtools/NEWS | 45 | ||||
| -rw-r--r-- | lib/testtools/testtools/matchers.py | 9 | ||||
| -rw-r--r-- | lib/testtools/testtools/testcase.py | 29 | ||||
| -rw-r--r-- | lib/testtools/testtools/tests/test_testtools.py | 32 | 
4 files changed, 78 insertions, 37 deletions
diff --git a/lib/testtools/NEWS b/lib/testtools/NEWS index dc5e6df8f1..596df0d6a6 100644 --- a/lib/testtools/NEWS +++ b/lib/testtools/NEWS @@ -4,6 +4,15 @@ testtools NEWS  NEXT  ~~~~ +Improvements +------------ + +* Code duplication between assertEqual and the matcher Equals has been removed. + +* In normal circumstances, a TestCase will no longer share details with clones +  of itself. (Andrew Bennetts, bug #637725) + +  0.9.6  ~~~~~ @@ -17,32 +26,32 @@ patches and TestCase.assertEqual gives slightly nicer errors.  Improvements  ------------ - * 'TestCase.assertEqual' now formats errors a little more nicely, in the -   style of bzrlib. +* 'TestCase.assertEqual' now formats errors a little more nicely, in the +  style of bzrlib. - * Added `PlaceHolder` and `ErrorHolder`, TestCase-like objects that can be -   used to add results to a `TestResult`. +* Added `PlaceHolder` and `ErrorHolder`, TestCase-like objects that can be +  used to add results to a `TestResult`. - * 'Mismatch' now takes optional description and details parameters, so -   custom Matchers aren't compelled to make their own subclass. +* 'Mismatch' now takes optional description and details parameters, so +  custom Matchers aren't compelled to make their own subclass. - * jml added a built-in UTF8_TEXT ContentType to make it slightly easier to -   add details to test results. See bug #520044. +* jml added a built-in UTF8_TEXT ContentType to make it slightly easier to +  add details to test results. See bug #520044. - * Fix a bug in our built-in matchers where assertThat would blow up if any -   of them failed. All built-in mismatch objects now provide get_details(). +* Fix a bug in our built-in matchers where assertThat would blow up if any +  of them failed. All built-in mismatch objects now provide get_details(). - * New 'Is' matcher, which lets you assert that a thing is identical to -   another thing. +* New 'Is' matcher, which lets you assert that a thing is identical to +  another thing. - * New 'LessThan' matcher which lets you assert that a thing is less than -   another thing. +* New 'LessThan' matcher which lets you assert that a thing is less than +  another thing. - * TestCase now has a 'patch()' method to make it easier to monkey-patching -   objects in tests. See the manual for more information. Fixes bug #310770. +* TestCase now has a 'patch()' method to make it easier to monkey-patching +  objects in tests. See the manual for more information. Fixes bug #310770. - * MultiTestResult methods now pass back return values from the results it -   forwards to. +* MultiTestResult methods now pass back return values from the results it +  forwards to.  0.9.5  ~~~~~ diff --git a/lib/testtools/testtools/matchers.py b/lib/testtools/testtools/matchers.py index 6a4c82a2fe..61b5bd74f9 100644 --- a/lib/testtools/testtools/matchers.py +++ b/lib/testtools/testtools/matchers.py @@ -25,6 +25,7 @@ __all__ = [  import doctest  import operator +from pprint import pformat  class Matcher(object): @@ -178,6 +179,14 @@ class _BinaryMismatch(Mismatch):          self.other = other      def describe(self): +        left = repr(self.expected) +        right = repr(self.other) +        if len(left) + len(right) > 70: +            return "%s:\nreference = %s\nactual = %s\n" % ( +                self._mismatch_string, pformat(self.expected), +                pformat(self.other)) +        else: +            return "%s %s %s" % (left, self._mismatch_string,right)          return "%r %s %r" % (self.expected, self._mismatch_string, self.other) diff --git a/lib/testtools/testtools/testcase.py b/lib/testtools/testtools/testcase.py index 48eec71d41..959c129691 100644 --- a/lib/testtools/testtools/testcase.py +++ b/lib/testtools/testtools/testcase.py @@ -17,13 +17,16 @@ try:  except ImportError:      wraps = None  import itertools -from pprint import pformat  import sys  import types  import unittest  from testtools import content  from testtools.compat import advance_iterator +from testtools.matchers import ( +    Annotate, +    Equals, +    )  from testtools.monkey import patch  from testtools.runtest import RunTest  from testtools.testresult import TestResult @@ -81,7 +84,9 @@ class TestCase(unittest.TestCase):          self._traceback_id_gen = itertools.count(0)          self.__setup_called = False          self.__teardown_called = False -        self.__details = {} +        # __details is lazy-initialized so that a constructed-but-not-run +        # TestCase is safe to use with clone_test_with_new_id. +        self.__details = None          self.__RunTest = kwargs.get('runTest', RunTest)          self.__exception_handlers = []          self.exception_handlers = [ @@ -114,6 +119,8 @@ class TestCase(unittest.TestCase):          :param content_object: The content object for this detail. See              testtools.content for more detail.          """ +        if self.__details is None: +            self.__details = {}          self.__details[name] = content_object      def getDetails(self): @@ -121,6 +128,8 @@ class TestCase(unittest.TestCase):          For more details see pydoc testtools.TestResult.          """ +        if self.__details is None: +            self.__details = {}          return self.__details      def patch(self, obj, attribute, value): @@ -230,18 +239,10 @@ class TestCase(unittest.TestCase):          :param observed: The observed value.          :param message: An optional message to include in the error.          """ -        try: -            return super(TestCase, self).assertEqual(expected, observed) -        except self.failureException: -            lines = [] -            if message: -                lines.append(message) -            lines.extend( -                ["not equal:", -                 "a = %s" % pformat(expected), -                 "b = %s" % pformat(observed), -                 '']) -            self.fail('\n'.join(lines)) +        matcher = Equals(expected) +        if message: +            matcher = Annotate(message, matcher) +        self.assertThat(observed, matcher)      failUnlessEqual = assertEquals = assertEqual diff --git a/lib/testtools/testtools/tests/test_testtools.py b/lib/testtools/testtools/tests/test_testtools.py index 9edc5a5176..5dfb355990 100644 --- a/lib/testtools/testtools/tests/test_testtools.py +++ b/lib/testtools/testtools/tests/test_testtools.py @@ -461,6 +461,15 @@ class TestAssertions(TestCase):               'a = %s' % pformat(a),               'b = %s' % pformat(b),               '']) +        expected_error = '\n'.join([ +            'Match failed. Matchee: "%r"' % b, +            'Matcher: Annotate(%r, Equals(%r))' % (message, a), +            'Difference: !=:', +            'reference = %s' % pformat(a), +            'actual = %s' % pformat(b), +            ': ' + message, +            '' +            ])          self.assertFails(expected_error, self.assertEqual, a, b, message)          self.assertFails(expected_error, self.assertEquals, a, b, message)          self.assertFails(expected_error, self.failUnlessEqual, a, b, message) @@ -468,11 +477,12 @@ class TestAssertions(TestCase):      def test_assertEqual_formatting_no_message(self):          a = "cat"          b = "dog" -        expected_error = '\n'.join( -            ['not equal:', -             'a = %s' % pformat(a), -             'b = %s' % pformat(b), -             '']) +        expected_error = '\n'.join([ +            'Match failed. Matchee: "dog"', +            'Matcher: Equals(\'cat\')', +            'Difference: \'cat\' != \'dog\'', +            '' +            ])          self.assertFails(expected_error, self.assertEqual, a, b)          self.assertFails(expected_error, self.assertEquals, a, b)          self.assertFails(expected_error, self.failUnlessEqual, a, b) @@ -760,6 +770,18 @@ class TestCloneTestWithNewId(TestCase):          self.assertEqual(oldName, test.id(),              "the original test instance should be unchanged.") +    def test_cloned_testcase_does_not_share_details(self): +        """A cloned TestCase does not share the details dict.""" +        class Test(TestCase): +            def test_foo(self): +                self.addDetail( +                    'foo', content.Content('text/plain', lambda: 'foo')) +        orig_test = Test('test_foo') +        cloned_test = clone_test_with_new_id(orig_test, self.getUniqueString()) +        orig_test.run(unittest.TestResult()) +        self.assertEqual('foo', orig_test.getDetails()['foo'].iter_bytes()) +        self.assertEqual(None, cloned_test.getDetails().get('foo')) +  class TestDetailsProvided(TestWithDetails):  | 
