From 52ed56cb60462141fe603fd27111fbfe4abce1b4 Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Tue, 3 Dec 2013 15:45:05 +0000 Subject: [PATCH] Move assertThat tests to separate module and apply them to assert_that as well. --- testtools/tests/__init__.py | 2 + testtools/tests/test_assert_that.py | 154 ++++++++++++++++++++++++++++ testtools/tests/test_testcase.py | 102 ------------------ 3 files changed, 156 insertions(+), 102 deletions(-) create mode 100644 testtools/tests/test_assert_that.py diff --git a/testtools/tests/__init__.py b/testtools/tests/__init__.py index db215ff..d40fcb3 100644 --- a/testtools/tests/__init__.py +++ b/testtools/tests/__init__.py @@ -9,6 +9,7 @@ from unittest import TestSuite def test_suite(): from testtools.tests import ( matchers, + test_assert_that, test_compat, test_content, test_content_type, @@ -27,6 +28,7 @@ def test_suite(): ) modules = [ matchers, + test_assert_that, test_compat, test_content, test_content_type, diff --git a/testtools/tests/test_assert_that.py b/testtools/tests/test_assert_that.py new file mode 100644 index 0000000..be3769d --- /dev/null +++ b/testtools/tests/test_assert_that.py @@ -0,0 +1,154 @@ +from doctest import ELLIPSIS + +from testtools import ( + TestCase, + ) +from testtools.assertions import ( + assert_that, + ) +from testtools.compat import ( + _u, + ) +from testtools.content import ( + TracebackContent, + ) +from testtools.matchers import ( + Annotate, + DocTestMatches, + Equals, + ) + + +class AssertThatTests(object): + """ + A mixin containing shared tests for assertThat and assert_that. + """ + + def assert_that_callable(self, *args, **kwargs): + raise NotImplementedError + + def assertFails(self, message, function, *args, **kwargs): + """Assert that function raises a failure with the given message.""" + failure = self.assertRaises( + self.failureException, function, *args, **kwargs) + self.assert_that_callable(failure, DocTestMatches(message, ELLIPSIS)) + + def test_assertThat_matches_clean(self): + class Matcher(object): + def match(self, foo): + return None + self.assert_that_callable("foo", Matcher()) + + def test_assertThat_mismatch_raises_description(self): + calls = [] + class Mismatch(object): + def __init__(self, thing): + self.thing = thing + def describe(self): + calls.append(('describe_diff', self.thing)) + return "object is not a thing" + def get_details(self): + return {} + class Matcher(object): + def match(self, thing): + calls.append(('match', thing)) + return Mismatch(thing) + def __str__(self): + calls.append(('__str__',)) + return "a description" + class Test(type(self)): + def test(self): + self.assert_that_callable("foo", Matcher()) + result = Test("test").run() + self.assertEqual([ + ('match', "foo"), + ('describe_diff', "foo"), + ], calls) + self.assertFalse(result.wasSuccessful()) + + def test_assertThat_output(self): + matchee = 'foo' + matcher = Equals('bar') + expected = matcher.match(matchee).describe() + self.assertFails(expected, self.assert_that_callable, matchee, matcher) + + def test_assertThat_message_is_annotated(self): + matchee = 'foo' + matcher = Equals('bar') + expected = Annotate('woo', matcher).match(matchee).describe() + self.assertFails(expected, + self.assert_that_callable, matchee, matcher, 'woo') + + def test_assertThat_verbose_output(self): + matchee = 'foo' + matcher = Equals('bar') + expected = ( + 'Match failed. Matchee: %r\n' + 'Matcher: %s\n' + 'Difference: %s\n' % ( + matchee, + matcher, + matcher.match(matchee).describe(), + )) + self.assertFails( + expected, + self.assert_that_callable, matchee, matcher, verbose=True) + + def get_error_string(self, e): + """Get the string showing how 'e' would be formatted in test output. + + This is a little bit hacky, since it's designed to give consistent + output regardless of Python version. + + In testtools, TestResult._exc_info_to_unicode is the point of dispatch + between various different implementations of methods that format + exceptions, so that's what we have to call. However, that method cares + about stack traces and formats the exception class. We don't care + about either of these, so we take its output and parse it a little. + """ + error = TracebackContent((e.__class__, e, None), self).as_text() + # We aren't at all interested in the traceback. + if error.startswith('Traceback (most recent call last):\n'): + lines = error.splitlines(True)[1:] + for i, line in enumerate(lines): + if not line.startswith(' '): + break + error = ''.join(lines[i:]) + # We aren't interested in how the exception type is formatted. + exc_class, error = error.split(': ', 1) + return error + + def test_assertThat_verbose_unicode(self): + # When assertThat is given matchees or matchers that contain non-ASCII + # unicode strings, we can still provide a meaningful error. + matchee = _u('\xa7') + matcher = Equals(_u('a')) + expected = ( + 'Match failed. Matchee: %s\n' + 'Matcher: %s\n' + 'Difference: %s\n\n' % ( + repr(matchee).replace("\\xa7", matchee), + matcher, + matcher.match(matchee).describe(), + )) + e = self.assertRaises( + self.failureException, self.assert_that_callable, matchee, matcher, + verbose=True) + self.assertEqual(expected, self.get_error_string(e)) + + +class TestAssertThatFunction(AssertThatTests, TestCase): + + def assert_that_callable(self, *args, **kwargs): + return assert_that(*args, **kwargs) + + +class TestAssertThatMethod(AssertThatTests, TestCase): + + def assert_that_callable(self, *args, **kwargs): + return self.assertThat(*args, **kwargs) + + +def test_suite(): + from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_testcase.py b/testtools/tests/test_testcase.py index 680368d..5561412 100644 --- a/testtools/tests/test_testcase.py +++ b/testtools/tests/test_testcase.py @@ -26,7 +26,6 @@ from testtools.compat import ( ) from testtools.content import ( text_content, - TracebackContent, ) from testtools.matchers import ( Annotate, @@ -510,65 +509,6 @@ class TestAssertions(TestCase): 'None matches Is(None): foo bar', self.assertIsNot, None, None, "foo bar") - def test_assertThat_matches_clean(self): - class Matcher(object): - def match(self, foo): - return None - self.assertThat("foo", Matcher()) - - def test_assertThat_mismatch_raises_description(self): - calls = [] - class Mismatch(object): - def __init__(self, thing): - self.thing = thing - def describe(self): - calls.append(('describe_diff', self.thing)) - return "object is not a thing" - def get_details(self): - return {} - class Matcher(object): - def match(self, thing): - calls.append(('match', thing)) - return Mismatch(thing) - def __str__(self): - calls.append(('__str__',)) - return "a description" - class Test(TestCase): - def test(self): - self.assertThat("foo", Matcher()) - result = Test("test").run() - self.assertEqual([ - ('match', "foo"), - ('describe_diff', "foo"), - ], calls) - self.assertFalse(result.wasSuccessful()) - - def test_assertThat_output(self): - matchee = 'foo' - matcher = Equals('bar') - expected = matcher.match(matchee).describe() - self.assertFails(expected, self.assertThat, matchee, matcher) - - def test_assertThat_message_is_annotated(self): - matchee = 'foo' - matcher = Equals('bar') - expected = Annotate('woo', matcher).match(matchee).describe() - self.assertFails(expected, self.assertThat, matchee, matcher, 'woo') - - def test_assertThat_verbose_output(self): - matchee = 'foo' - matcher = Equals('bar') - expected = ( - 'Match failed. Matchee: %r\n' - 'Matcher: %s\n' - 'Difference: %s\n' % ( - matchee, - matcher, - matcher.match(matchee).describe(), - )) - self.assertFails( - expected, self.assertThat, matchee, matcher, verbose=True) - def test__force_failure_fails_test(self): class Test(TestCase): def test_foo(self): @@ -579,48 +519,6 @@ class TestAssertions(TestCase): self.assertFalse(result.wasSuccessful()) self.assertTrue(test.remaining_code_run) - def get_error_string(self, e): - """Get the string showing how 'e' would be formatted in test output. - - This is a little bit hacky, since it's designed to give consistent - output regardless of Python version. - - In testtools, TestResult._exc_info_to_unicode is the point of dispatch - between various different implementations of methods that format - exceptions, so that's what we have to call. However, that method cares - about stack traces and formats the exception class. We don't care - about either of these, so we take its output and parse it a little. - """ - error = TracebackContent((e.__class__, e, None), self).as_text() - # We aren't at all interested in the traceback. - if error.startswith('Traceback (most recent call last):\n'): - lines = error.splitlines(True)[1:] - for i, line in enumerate(lines): - if not line.startswith(' '): - break - error = ''.join(lines[i:]) - # We aren't interested in how the exception type is formatted. - exc_class, error = error.split(': ', 1) - return error - - def test_assertThat_verbose_unicode(self): - # When assertThat is given matchees or matchers that contain non-ASCII - # unicode strings, we can still provide a meaningful error. - matchee = _u('\xa7') - matcher = Equals(_u('a')) - expected = ( - 'Match failed. Matchee: %s\n' - 'Matcher: %s\n' - 'Difference: %s\n\n' % ( - repr(matchee).replace("\\xa7", matchee), - matcher, - matcher.match(matchee).describe(), - )) - e = self.assertRaises( - self.failureException, self.assertThat, matchee, matcher, - verbose=True) - self.assertEqual(expected, self.get_error_string(e)) - def test_assertEqual_nice_formatting(self): message = "These things ought not be equal." a = ['apple', 'banana', 'cherry']