testtools-dev team mailing list archive
-
testtools-dev team
-
Mailing list archive
-
Message #00859
[Merge] lp:~jml/testtools/expected-exception-791889 into lp:testtools
Jonathan Lange has proposed merging lp:~jml/testtools/expected-exception-791889 into lp:testtools.
Requested reviews:
testtools committers (testtools-committers)
Related bugs:
Bug #791889 in testtools: "The value_re argument for ExpectedException only fits some situations"
https://bugs.launchpad.net/testtools/+bug/791889
For more details, see:
https://code.launchpad.net/~jml/testtools/expected-exception-791889/+merge/69546
This patch *actually* fixes bug 791889. Really.
In https://code.launchpad.net/~jml/testtools/matches-exception-791889/+merge/68595, I had assumed that the bug was about MatchesException. Luckily, the changes in that branch were also worthwhile, and made this branch much easier.
What we do here is make ExpectedException delegate to MatchesException for its logic. Neat, huh?
Also took the opportunity to tweak AfterPreprocessing so we can use it "silently", such that it doesn't leave a trace of itself in the mismatch. For the cases here, it actually makes the errors clearer.
--
https://code.launchpad.net/~jml/testtools/expected-exception-791889/+merge/69546
Your team testtools developers is subscribed to branch lp:testtools.
=== modified file 'testtools/matchers.py'
--- testtools/matchers.py 2011-07-20 21:07:45 +0000
+++ testtools/matchers.py 2011-07-27 19:57:29 +0000
@@ -421,7 +421,7 @@
Matcher.__init__(self)
self.expected = exception
if istext(value_re):
- value_re = AfterPreproccessing(str, MatchesRegex(value_re))
+ value_re = AfterPreproccessing(str, MatchesRegex(value_re), False)
self.value_re = value_re
self._is_instance = type(self.expected) not in classtypes()
@@ -824,9 +824,19 @@
return AfterPreprocessing(_read, Equals(content))
"""
- def __init__(self, preprocessor, matcher):
+ def __init__(self, preprocessor, matcher, annotate=True):
+ """Create an AfterPreprocessing matcher.
+
+ :param preprocessor: A function called with the matchee before
+ matching.
+ :param matcher: What to match the preprocessed matchee against.
+ :param annotate: Whether or not to annotate the matcher with
+ something explaining how we transformed the matchee. Defaults
+ to True.
+ """
self.preprocessor = preprocessor
self.matcher = matcher
+ self.annotate = annotate
def _str_preprocessor(self):
if isinstance(self.preprocessor, types.FunctionType):
@@ -839,9 +849,13 @@
def match(self, value):
after = self.preprocessor(value)
- return Annotate(
- "after %s on %r" % (self._str_preprocessor(), value),
- self.matcher).match(after)
+ if self.annotate:
+ matcher = Annotate(
+ "after %s on %r" % (self._str_preprocessor(), value),
+ self.matcher)
+ else:
+ matcher = self.matcher
+ return matcher.match(after)
# This is the old, deprecated. spelling of the name, kept for backwards
# compatibility.
=== modified file 'testtools/testcase.py'
--- testtools/testcase.py 2011-07-21 09:39:52 +0000
+++ testtools/testcase.py 2011-07-27 19:57:29 +0000
@@ -16,7 +16,6 @@
import copy
import itertools
-import re
import sys
import types
import unittest
@@ -29,6 +28,7 @@
from testtools.matchers import (
Annotate,
Equals,
+ MatchesException,
Is,
Not,
)
@@ -754,7 +754,7 @@
exception is raised, an AssertionError will be raised.
"""
- def __init__(self, exc_type, value_re):
+ def __init__(self, exc_type, value_re=None):
"""Construct an `ExpectedException`.
:param exc_type: The type of exception to expect.
@@ -772,9 +772,11 @@
raise AssertionError('%s not raised.' % self.exc_type.__name__)
if exc_type != self.exc_type:
return False
- if not re.match(self.value_re, str(exc_value)):
- raise AssertionError('"%s" does not match "%s".' %
- (str(exc_value), self.value_re))
+ if self.value_re:
+ matcher = MatchesException(self.exc_type, self.value_re)
+ mismatch = matcher.match((exc_type, exc_value, traceback))
+ if mismatch:
+ raise AssertionError(mismatch.describe())
return True
=== modified file 'testtools/tests/test_matchers.py'
--- testtools/tests/test_matchers.py 2011-07-21 09:39:52 +0000
+++ testtools/tests/test_matchers.py 2011-07-27 19:57:29 +0000
@@ -258,9 +258,7 @@
MatchesException(Exception, 'fo.'))
]
describe_examples = [
- # XXX: This is kind of a crappy message. Need to change
- # AfterPreproccessing.
- ("'bar' does not match 'fo.': after <type 'str'> on ValueError('bar',)",
+ ("'bar' does not match 'fo.'",
error_bar, MatchesException(ValueError, "fo.")),
]
@@ -736,9 +734,10 @@
]
describe_examples = [
- ("1 != 0: after <function parity> on 2",
- 2,
+ ("1 != 0: after <function parity> on 2", 2,
AfterPreprocessing(parity, Equals(1))),
+ ("1 != 0", 2,
+ AfterPreprocessing(parity, Equals(1), annotate=False)),
]
=== modified file 'testtools/tests/test_with_with.py'
--- testtools/tests/test_with_with.py 2011-04-20 23:45:52 +0000
+++ testtools/tests/test_with_with.py 2011-07-27 19:57:29 +0000
@@ -8,6 +8,11 @@
ExpectedException,
TestCase,
)
+from testtools.matchers import (
+ AfterPreprocessing,
+ Equals,
+ )
+
class TestExpectedException(TestCase):
"""Test the ExpectedException context manager."""
@@ -16,13 +21,30 @@
with ExpectedException(ValueError, 'tes.'):
raise ValueError('test')
+ def test_pass_on_raise_matcher(self):
+ with ExpectedException(
+ ValueError, AfterPreprocessing(str, Equals('test'))):
+ raise ValueError('test')
+
def test_raise_on_text_mismatch(self):
try:
with ExpectedException(ValueError, 'tes.'):
raise ValueError('mismatch')
except AssertionError:
e = sys.exc_info()[1]
- self.assertEqual('"mismatch" does not match "tes.".', str(e))
+ self.assertEqual("'mismatch' does not match 'tes.'", str(e))
+ else:
+ self.fail('AssertionError not raised.')
+
+ def test_raise_on_general_mismatch(self):
+ matcher = AfterPreprocessing(str, Equals('test'))
+ value_error = ValueError('mismatch')
+ try:
+ with ExpectedException(ValueError, matcher):
+ raise value_error
+ except AssertionError:
+ e = sys.exc_info()[1]
+ self.assertEqual(matcher.match(value_error).describe(), str(e))
else:
self.fail('AssertionError not raised.')
@@ -45,3 +67,7 @@
self.assertEqual('TypeError not raised.', str(e))
else:
self.fail('AssertionError not raised.')
+
+ def test_pass_on_raise_any_message(self):
+ with ExpectedException(ValueError):
+ raise ValueError('whatever')
Follow ups