testtools-dev team mailing list archive
-
testtools-dev team
-
Mailing list archive
-
Message #00271
[Merge] lp:~lifeless/testtools/matchers into lp:testtools
Robert Collins has proposed merging lp:~lifeless/testtools/matchers into lp:testtools.
Requested reviews:
testtools developers (testtools-dev)
This adds polished matchers to replace assertRaises, which I first prototyped in testrepository.
The final commit on the branch uses the new Raises and MatchesException (ugh, perhaps a shorter name would be good?) throughout testtools own tests (except where we are testing assertRaises itself).
I included this (largely mechanical) change in the branch so that the new matchers can be asessed properly. I considered a sugar to replace lambda: but actually found that the lambda: spelling grew on me pretty quickly: its much clearer about *what is being called* than the magic call-forward approach that assertRaises uses.
--
https://code.launchpad.net/~lifeless/testtools/matchers/+merge/40606
Your team testtools developers is requested to review the proposed merge of lp:~lifeless/testtools/matchers into lp:testtools.
=== modified file 'MANUAL'
--- MANUAL 2010-10-26 18:59:12 +0000
+++ MANUAL 2010-11-11 09:52:45 +0000
@@ -150,6 +150,13 @@
self.assertEqual('bob', error.username)
self.assertEqual('User bob cannot frobnicate', str(error))
+Note that this is incompatible with the assertRaises in unittest2/Python2.7.
+While we have no immediate plans to change to be compatible consider using the
+new assertThat facility instead::
+
+ self.assertThat(thing.frobnicate,
+ Raises(MatchesException(UnauthorisedError('bob')))
+
TestCase.assertThat
~~~~~~~~~~~~~~~~~~~
=== modified file 'Makefile'
--- Makefile 2010-07-04 07:19:58 +0000
+++ Makefile 2010-11-11 09:52:45 +0000
@@ -16,15 +16,20 @@
rm -f TAGS tags
find testtools -name "*.pyc" -exec rm '{}' \;
-release:
+prerelease:
# An existing MANIFEST breaks distutils sometimes. Avoid that.
-rm MANIFEST
+
+release:
./setup.py sdist upload --sign
+snapshot: prerelease
+ ./setup.py sdist
+
apidocs:
pydoctor --make-html --add-package testtools \
--docformat=restructuredtext --project-name=testtools \
--project-url=https://launchpad.net/testtools
-.PHONY: check clean release apidocs
+.PHONY: check clean prerelease release apidocs
=== modified file 'NEWS'
--- NEWS 2010-10-31 16:42:36 +0000
+++ NEWS 2010-11-11 09:52:45 +0000
@@ -7,6 +7,9 @@
Improvements
------------
+* addUnexpectedSuccess is translated to addFailure for test results that don't
+ know about addUnexpectedSuccess. (Jonathan Lange, #654474)
+
* Experimental support for running tests that return Deferreds.
(Jonathan Lange, Martin [gz])
@@ -21,8 +24,12 @@
* Malformed SyntaxErrors no longer blow up the test suite. (Martin [gz])
-* addUnexpectedSuccess is translated to addFailure for test results that don't
- know about addUnexpectedSuccess. (Jonathan Lange, #654474)
+* ``MatchesException`` added to the ``testtools.matchers`` module - matches
+ an exception class and parameters. (Robert Collins)
+
+* ``Raises`` added to the ``testtools.matchers`` module - matches if the
+ supplied callable raises, and delegates to an optional matcher for validation
+ of the exception. (Robert Collins)
* ``testools.TestCase.useFixture`` has been added to glue with fixtures nicely.
(Robert Collins)
=== modified file 'testtools/matchers.py'
--- testtools/matchers.py 2010-10-31 16:25:47 +0000
+++ testtools/matchers.py 2010-11-11 09:52:45 +0000
@@ -20,14 +20,17 @@
'LessThan',
'MatchesAll',
'MatchesAny',
+ 'MatchesException',
'NotEquals',
'Not',
+ 'Raises',
'StartsWith',
]
import doctest
import operator
from pprint import pformat
+import sys
class Matcher(object):
@@ -103,6 +106,10 @@
"""
return getattr(self, '_details', {})
+ def __repr__(self):
+ return "<testtools.matchers.Mismatch object at %x attributes=%r>" % (
+ id(self), self.__dict__)
+
class DocTestMatches(object):
"""See if a string matches a doctest example."""
@@ -323,6 +330,40 @@
return "%r matches %s" % (self.other, self.matcher)
+class MatchesException(Matcher):
+ """Match an exc_info tuple against an exception instance or type."""
+
+ def __init__(self, exception):
+ """Create a MatchesException that will match exc_info's for exception.
+
+ :param exception: Either an exception instance or type.
+ If an instance is given, the type and arguments of the exception
+ are checked. If a type is given only the type of the exception is
+ checked.
+ """
+ Matcher.__init__(self)
+ self.expected = exception
+
+ def _expected_type(self):
+ if type(self.expected) is type:
+ return self.expected
+ return type(self.expected)
+
+ def match(self, other):
+ if type(other) != tuple:
+ return Mismatch('%r is not an exc_info tuple' % other)
+ if not issubclass(other[0], self._expected_type()):
+ return Mismatch('%r is not a %r' % (
+ other[0], self._expected_type()))
+ if (type(self.expected) is not type and
+ other[1].args != self.expected.args):
+ return Mismatch('%r has different arguments to %r.' % (
+ other[1], self.expected))
+
+ def __str__(self):
+ return "MatchesException(%r)" % self.expected
+
+
class StartsWith(Matcher):
"""Checks whether one string starts with another."""
@@ -398,3 +439,46 @@
def describe(self):
return '%s: %s' % (self.mismatch.describe(), self.annotation)
+
+
+class Raises(Matcher):
+ """Match if the matchee raises an exception when called.
+
+ Exceptions which are not subclasses of Exception propogate out of the
+ Raises.match call unless they are explicitly matched.
+ """
+
+ def __init__(self, exception_matcher=None):
+ """Create a Raises matcher.
+
+ :param exception_matcher: Optional validator for the exception raised
+ by matchee. If supplied the exc_info tuple for the exception raised
+ is passed into that matcher. If no exception_matcher is supplied
+ then the simple fact of raising an exception is considered enough
+ to match on.
+ """
+ self.exception_matcher = exception_matcher
+
+ def match(self, matchee):
+ try:
+ result = matchee()
+ return Mismatch('%r returned %r' % (matchee, result))
+ # Catch all exceptions: Raises() should be able to match a
+ # KeyboardInterrupt or SystemExit.
+ except:
+ exc_info = sys.exc_info()
+ if self.exception_matcher:
+ mismatch = self.exception_matcher.match(sys.exc_info())
+ if not mismatch:
+ return
+ else:
+ mismatch = None
+ # The exception did not match, or no explicit matching logic was
+ # performed. If the exception is a non-user exception (that is, not
+ # a subclass of Exception) then propogate it.
+ if not issubclass(exc_info[0], Exception):
+ raise exc_info[0], exc_info[1], exc_info[2]
+ return mismatch
+
+ def __str__(self):
+ return 'Raises()'
=== modified file 'testtools/tests/test_compat.py'
--- testtools/tests/test_compat.py 2010-06-23 00:32:51 +0000
+++ testtools/tests/test_compat.py 2010-11-11 09:52:45 +0000
@@ -17,6 +17,10 @@
_u,
unicode_output_stream,
)
+from testtools.matchers import (
+ MatchesException,
+ Raises,
+ )
class TestDetectEncoding(testtools.TestCase):
@@ -241,7 +245,8 @@
soutwrapper = unicode_output_stream(sout)
if newio:
self.expectFailure("Python 3 StringIO expects text not bytes",
- self.assertRaises, TypeError, soutwrapper.write, self.uni)
+ self.assertThat, lambda: soutwrapper.write(self.uni),
+ Raises(MatchesException(TypeError)))
soutwrapper.write(self.uni)
self.assertEqual("pa???n", sout.getvalue())
=== modified file 'testtools/tests/test_content.py'
--- testtools/tests/test_content.py 2010-10-28 22:06:41 +0000
+++ testtools/tests/test_content.py 2010-11-11 09:52:45 +0000
@@ -5,16 +5,21 @@
from testtools.compat import _u
from testtools.content import Content, TracebackContent, text_content
from testtools.content_type import ContentType, UTF8_TEXT
+from testtools.matchers import MatchesException, Raises
from testtools.tests.helpers import an_exc_info
+raises_value = Raises(MatchesException(ValueError))
+
+
class TestContent(TestCase):
def test___init___None_errors(self):
- self.assertRaises(ValueError, Content, None, None)
- self.assertRaises(ValueError, Content, None, lambda: ["traceback"])
- self.assertRaises(ValueError, Content,
- ContentType("text", "traceback"), None)
+ self.assertThat(lambda:Content(None, None), raises_value)
+ self.assertThat(lambda:Content(None, lambda: ["traceback"]),
+ raises_value)
+ self.assertThat(lambda:Content(ContentType("text", "traceback"), None),
+ raises_value)
def test___init___sets_ivars(self):
content_type = ContentType("foo", "bar")
@@ -37,7 +42,7 @@
def test_iter_text_not_text_errors(self):
content_type = ContentType("foo", "bar")
content = Content(content_type, lambda: ["bytes"])
- self.assertRaises(ValueError, content.iter_text)
+ self.assertThat(content.iter_text, raises_value)
def test_iter_text_decodes(self):
content_type = ContentType("text", "strange", {"charset": "utf8"})
@@ -56,7 +61,7 @@
class TestTracebackContent(TestCase):
def test___init___None_errors(self):
- self.assertRaises(ValueError, TracebackContent, None, None)
+ self.assertThat(lambda:TracebackContent(None, None), raises_value)
def test___init___sets_ivars(self):
content = TracebackContent(an_exc_info, self)
=== modified file 'testtools/tests/test_content_type.py'
--- testtools/tests/test_content_type.py 2010-08-05 10:04:56 +0000
+++ testtools/tests/test_content_type.py 2010-11-11 09:52:45 +0000
@@ -1,16 +1,17 @@
# Copyright (c) 2008 Jonathan M. Lange. See LICENSE for details.
from testtools import TestCase
-from testtools.matchers import Equals
+from testtools.matchers import Equals, MatchesException, Raises
from testtools.content_type import ContentType, UTF8_TEXT
class TestContentType(TestCase):
def test___init___None_errors(self):
- self.assertRaises(ValueError, ContentType, None, None)
- self.assertRaises(ValueError, ContentType, None, "traceback")
- self.assertRaises(ValueError, ContentType, "text", None)
+ raises_value = Raises(MatchesException(ValueError))
+ self.assertThat(lambda:ContentType(None, None), raises_value)
+ self.assertThat(lambda:ContentType(None, "traceback"), raises_value)
+ self.assertThat(lambda:ContentType("text", None), raises_value)
def test___init___sets_ivars(self):
content_type = ContentType("foo", "bar")
=== modified file 'testtools/tests/test_deferredruntest.py'
--- testtools/tests/test_deferredruntest.py 2010-10-31 16:33:44 +0000
+++ testtools/tests/test_deferredruntest.py 2010-11-11 09:52:45 +0000
@@ -22,6 +22,8 @@
from testtools.matchers import (
Equals,
KeysEqual,
+ MatchesException,
+ Raises,
)
from testtools.runtest import RunTest
@@ -396,7 +398,8 @@
runner = self.make_runner(test, timeout * 5)
result = self.make_result()
reactor.callLater(timeout, os.kill, os.getpid(), SIGINT)
- self.assertRaises(KeyboardInterrupt, runner.run, result)
+ self.assertThat(lambda:runner.run(result),
+ Raises(MatchesException(KeyboardInterrupt)))
@skipIf(os.name != "posix", "Sending SIGINT with os.kill is posix only")
def test_fast_keyboard_interrupt_stops_test_run(self):
@@ -414,7 +417,8 @@
runner = self.make_runner(test, timeout * 5)
result = self.make_result()
reactor.callWhenRunning(os.kill, os.getpid(), SIGINT)
- self.assertRaises(KeyboardInterrupt, runner.run, result)
+ self.assertThat(lambda:runner.run(result),
+ Raises(MatchesException(KeyboardInterrupt)))
def test_timeout_causes_test_error(self):
# If a test times out, it reports itself as having failed with a
=== modified file 'testtools/tests/test_matchers.py'
--- testtools/tests/test_matchers.py 2010-10-31 16:25:47 +0000
+++ testtools/tests/test_matchers.py 2010-11-11 09:52:45 +0000
@@ -3,6 +3,7 @@
"""Tests for matchers."""
import doctest
+import sys
from testtools import (
Matcher, # check that Matcher is exposed at the top level for docs.
@@ -18,9 +19,11 @@
LessThan,
MatchesAny,
MatchesAll,
+ MatchesException,
Mismatch,
Not,
NotEquals,
+ Raises,
StartsWith,
)
@@ -37,7 +40,8 @@
def test_constructor_no_arguments(self):
mismatch = Mismatch()
- self.assertRaises(NotImplementedError, mismatch.describe)
+ self.assertThat(mismatch.describe,
+ Raises(MatchesException(NotImplementedError)))
self.assertEqual({}, mismatch.get_details())
@@ -155,6 +159,58 @@
describe_examples = [('4 is >= 4', 4, LessThan(4))]
+def make_error(type, *args, **kwargs):
+ try:
+ raise type(*args, **kwargs)
+ except type:
+ return sys.exc_info()
+
+
+class TestMatchesExceptionInstanceInterface(TestCase, TestMatchersInterface):
+
+ matches_matcher = MatchesException(ValueError("foo"))
+ error_foo = make_error(ValueError, 'foo')
+ error_bar = make_error(ValueError, 'bar')
+ error_base_foo = make_error(Exception, 'foo')
+ matches_matches = [error_foo]
+ matches_mismatches = [error_bar, error_base_foo]
+
+ str_examples = [
+ ("MatchesException(Exception('foo',))",
+ MatchesException(Exception('foo')))
+ ]
+ describe_examples = [
+ ("<type 'exceptions.Exception'> is not a "
+ "<type 'exceptions.ValueError'>",
+ error_base_foo,
+ MatchesException(ValueError("foo"))),
+ ("ValueError('bar',) has different arguments to ValueError('foo',).",
+ error_bar,
+ MatchesException(ValueError("foo"))),
+ ]
+
+
+class TestMatchesExceptionTypeInterface(TestCase, TestMatchersInterface):
+
+ matches_matcher = MatchesException(ValueError)
+ error_foo = make_error(ValueError, 'foo')
+ error_sub = make_error(UnicodeError, 'bar')
+ error_base_foo = make_error(Exception, 'foo')
+ matches_matches = [error_foo, error_sub]
+ matches_mismatches = [error_base_foo]
+
+ str_examples = [
+ ("MatchesException(<type 'exceptions.Exception'>)",
+ MatchesException(Exception))
+ ]
+ describe_examples = [
+ ("<type 'exceptions.Exception'> is not a "
+ "<type 'exceptions.ValueError'>",
+ error_base_foo,
+ MatchesException(ValueError)),
+ ]
+
+
class TestNotInterface(TestCase, TestMatchersInterface):
matches_matcher = Not(Equals(1))
@@ -249,6 +305,67 @@
describe_examples = [("1 != 2: foo", 2, Annotate('foo', Equals(1)))]
+class TestRaisesInterface(TestCase, TestMatchersInterface):
+
+ matches_matcher = Raises()
+ def boom():
+ raise Exception('foo')
+ matches_matches = [boom]
+ matches_mismatches = [lambda:None]
+
+ # Tricky to get function objects to render constantly, and the interfaces
+ # helper uses assertEqual rather than (for instance) DocTestMatches.
+ str_examples = []
+
+ describe_examples = []
+
+
+class TestRaisesExceptionMatcherInterface(TestCase, TestMatchersInterface):
+
+ matches_matcher = Raises(
+ exception_matcher=MatchesException(Exception('foo')))
+ def boom_bar():
+ raise Exception('bar')
+ def boom_foo():
+ raise Exception('foo')
+ matches_matches = [boom_foo]
+ matches_mismatches = [lambda:None, boom_bar]
+
+ # Tricky to get function objects to render constantly, and the interfaces
+ # helper uses assertEqual rather than (for instance) DocTestMatches.
+ str_examples = []
+
+ describe_examples = []
+
+
+class TestRaisesBaseTypes(TestCase):
+
+ def raiser(self):
+ raise KeyboardInterrupt('foo')
+
+ def test_KeyboardInterrupt_matched(self):
+ # When KeyboardInterrupt is matched, it is swallowed.
+ matcher = Raises(MatchesException(KeyboardInterrupt))
+ self.assertThat(self.raiser, matcher)
+
+ def test_KeyboardInterrupt_propogates(self):
+ # The default 'it raised' propogates KeyboardInterrupt.
+ match_keyb = Raises(MatchesException(KeyboardInterrupt))
+ def raise_keyb_from_match():
+ matcher = Raises()
+ matcher.match(self.raiser)
+ self.assertThat(raise_keyb_from_match, match_keyb)
+
+ def test_KeyboardInterrupt_match_Exception_propogates(self):
+ # If the raised exception isn't matched, and it is not a subclass of
+ # Exception, it is propogated.
+ match_keyb = Raises(MatchesException(KeyboardInterrupt))
+ def raise_keyb_from_match():
+ matcher = Raises(MatchesException(Exception))
+ matcher.match(self.raiser)
+ self.assertThat(raise_keyb_from_match, match_keyb)
+
+
class DoesNotStartWithTests(TestCase):
def test_describe(self):
=== modified file 'testtools/tests/test_monkey.py'
--- testtools/tests/test_monkey.py 2010-10-21 15:25:42 +0000
+++ testtools/tests/test_monkey.py 2010-11-11 09:52:45 +0000
@@ -4,6 +4,7 @@
"""Tests for testtools.monkey."""
from testtools import TestCase
+from testtools.matchers import MatchesException, Raises
from testtools.monkey import MonkeyPatcher, patch
@@ -137,8 +138,8 @@
self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha')
self.monkey_patcher.add_patch(self.test_object, 'bar', 'blahblah')
- self.assertRaises(
- RuntimeError, self.monkey_patcher.run_with_patches, _)
+ self.assertThat(lambda:self.monkey_patcher.run_with_patches(_),
+ Raises(MatchesException(RuntimeError("Something went wrong!"))))
self.assertEquals(self.test_object.foo, self.original_object.foo)
self.assertEquals(self.test_object.bar, self.original_object.bar)
=== modified file 'testtools/tests/test_runtest.py'
--- testtools/tests/test_runtest.py 2010-10-25 16:42:32 +0000
+++ testtools/tests/test_runtest.py 2010-11-11 09:52:45 +0000
@@ -9,7 +9,7 @@
TestCase,
TestResult,
)
-from testtools.matchers import Is
+from testtools.matchers import MatchesException, Is, Raises
from testtools.tests.helpers import ExtendedTestResult
@@ -64,7 +64,8 @@
raise KeyboardInterrupt("yo")
run = RunTest(case, None)
run.result = ExtendedTestResult()
- self.assertRaises(KeyboardInterrupt, run._run_user, raises)
+ self.assertThat(lambda: run._run_user(raises),
+ Raises(MatchesException(KeyboardInterrupt)))
self.assertEqual([], run.result._events)
def test__run_user_calls_onException(self):
@@ -109,7 +110,8 @@
log.append((result, err))
run = RunTest(case, [(ValueError, log_exc)])
run.result = ExtendedTestResult()
- self.assertRaises(KeyError, run._run_user, raises)
+ self.assertThat(lambda: run._run_user(raises),
+ Raises(MatchesException(KeyError)))
self.assertEqual([], run.result._events)
self.assertEqual([], log)
@@ -128,7 +130,8 @@
log.append((result, err))
run = RunTest(case, [(ValueError, log_exc)])
run.result = ExtendedTestResult()
- self.assertRaises(ValueError, run._run_user, raises)
+ self.assertThat(lambda: run._run_user(raises),
+ Raises(MatchesException(ValueError)))
self.assertEqual([], run.result._events)
self.assertEqual([], log)
@@ -171,7 +174,8 @@
raise Exception("foo")
run = RunTest(case, lambda x: x)
run._run_core = inner
- self.assertRaises(Exception, run.run, result)
+ self.assertThat(lambda: run.run(result),
+ Raises(MatchesException(Exception("foo"))))
self.assertEqual([
('startTest', case),
('stopTest', case),
=== modified file 'testtools/tests/test_spinner.py'
--- testtools/tests/test_spinner.py 2010-10-26 10:42:59 +0000
+++ testtools/tests/test_spinner.py 2010-11-11 09:52:45 +0000
@@ -12,6 +12,8 @@
from testtools.matchers import (
Equals,
Is,
+ MatchesException,
+ Raises,
)
from testtools._spinner import (
DeferredNotFired,
@@ -40,7 +42,7 @@
calls.append(None)
if len(calls) < 5:
log_something()
- self.assertRaises(ReentryError, log_something)
+ self.assertThat(log_something, Raises(MatchesException(ReentryError)))
self.assertEqual(1, len(calls))
def test_deeper_stack(self):
@@ -55,7 +57,7 @@
calls.append(None)
if len(calls) < 5:
g()
- self.assertRaises(ReentryError, f)
+ self.assertThat(f, Raises(MatchesException(ReentryError)))
self.assertEqual(2, len(calls))
@@ -64,7 +66,8 @@
def test_not_fired(self):
# extract_result raises DeferredNotFired if it's given a Deferred that
# has not fired.
- self.assertRaises(DeferredNotFired, extract_result, defer.Deferred())
+ self.assertThat(lambda:extract_result(defer.Deferred()),
+ Raises(MatchesException(DeferredNotFired)))
def test_success(self):
# extract_result returns the value of the Deferred if it has fired
@@ -81,7 +84,8 @@
except ZeroDivisionError:
f = Failure()
d = defer.fail(f)
- self.assertRaises(ZeroDivisionError, extract_result, d)
+ self.assertThat(lambda:extract_result(d),
+ Raises(MatchesException(ZeroDivisionError)))
class TestTrapUnhandledErrors(TestCase):
@@ -137,9 +141,9 @@
def test_exception_reraised(self):
# If the given function raises an error, run_in_reactor re-raises that
# error.
- self.assertRaises(
- ZeroDivisionError,
- self.make_spinner().run, self.make_timeout(), lambda: 1 / 0)
+ self.assertThat(
+ lambda:self.make_spinner().run(self.make_timeout(), lambda: 1/0),
+ Raises(MatchesException(ZeroDivisionError)))
def test_keyword_arguments(self):
# run_in_reactor passes keyword arguments on.
@@ -152,10 +156,9 @@
# run_in_reactor raises an error if it is called inside another call
# to run_in_reactor.
spinner = self.make_spinner()
- self.assertRaises(
- ReentryError,
- spinner.run, self.make_timeout(),
- spinner.run, self.make_timeout(), lambda: None)
+ self.assertThat(lambda: spinner.run(
+ self.make_timeout(), spinner.run, self.make_timeout(), lambda: None),
+ Raises(MatchesException(ReentryError)))
def test_deferred_value_returned(self):
# If the given function returns a Deferred, run_in_reactor returns the
@@ -181,9 +184,9 @@
def test_timeout(self):
# If the function takes too long to run, we raise a TimeoutError.
timeout = self.make_timeout()
- self.assertRaises(
- TimeoutError,
- self.make_spinner().run, timeout, lambda: defer.Deferred())
+ self.assertThat(
+ lambda:self.make_spinner().run(timeout, lambda: defer.Deferred()),
+ Raises(MatchesException(TimeoutError)))
def test_no_junk_by_default(self):
# If the reactor hasn't spun yet, then there cannot be any junk.
@@ -259,8 +262,8 @@
spinner = self.make_spinner(reactor)
timeout = self.make_timeout()
spinner.run(timeout, reactor.listenTCP, 0, ServerFactory())
- self.assertRaises(
- StaleJunkError, spinner.run, timeout, lambda: None)
+ self.assertThat(lambda: spinner.run(timeout, lambda: None),
+ Raises(MatchesException(StaleJunkError)))
def test_clear_junk_clears_previous_junk(self):
# If 'run' is called and there's still junk in the spinner's junk
@@ -284,8 +287,8 @@
spinner = self.make_spinner(reactor)
timeout = self.make_timeout()
reactor.callLater(timeout, os.kill, os.getpid(), SIGINT)
- self.assertRaises(
- NoResultError, spinner.run, timeout * 5, defer.Deferred)
+ self.assertThat(lambda:spinner.run(timeout * 5, defer.Deferred),
+ Raises(MatchesException(NoResultError)))
self.assertEqual([], spinner._clean())
@skipIf(os.name != "posix", "Sending SIGINT with os.kill is posix only")
@@ -305,8 +308,8 @@
spinner = self.make_spinner(reactor)
timeout = self.make_timeout()
reactor.callWhenRunning(os.kill, os.getpid(), SIGINT)
- self.assertRaises(
- NoResultError, spinner.run, timeout * 5, defer.Deferred)
+ self.assertThat(lambda:spinner.run(timeout * 5, defer.Deferred),
+ Raises(MatchesException(NoResultError)))
self.assertEqual([], spinner._clean())
@skipIf(os.name != "posix", "Sending SIGINT with os.kill is posix only")
=== modified file 'testtools/tests/test_testresult.py'
--- testtools/tests/test_testresult.py 2010-10-26 10:19:56 +0000
+++ testtools/tests/test_testresult.py 2010-11-11 09:52:45 +0000
@@ -36,7 +36,11 @@
)
from testtools.content import Content
from testtools.content_type import ContentType, UTF8_TEXT
-from testtools.matchers import DocTestMatches
+from testtools.matchers import (
+ DocTestMatches,
+ MatchesException,
+ Raises,
+ )
from testtools.tests.helpers import (
LoggingResult,
Python26TestResult,
@@ -760,8 +764,9 @@
def test_outcome__no_details(self):
self.make_extended_result()
- self.assertRaises(ValueError,
- getattr(self.converter, self.outcome), self)
+ self.assertThat(
+ lambda: getattr(self.converter, self.outcome)(self),
+ Raises(MatchesException(ValueError)))
class TestExtendedToOriginalAddFailure(
@@ -821,8 +826,9 @@
def test_outcome__no_details(self):
self.make_extended_result()
- self.assertRaises(ValueError,
- getattr(self.converter, self.outcome), self)
+ self.assertThat(
+ lambda: getattr(self.converter, self.outcome)(self),
+ Raises(MatchesException(ValueError)))
class TestExtendedToOriginalAddSuccess(
=== modified file 'testtools/tests/test_testtools.py'
--- testtools/tests/test_testtools.py 2010-10-28 20:18:39 +0000
+++ testtools/tests/test_testtools.py 2010-11-11 09:52:45 +0000
@@ -20,6 +20,8 @@
)
from testtools.matchers import (
Equals,
+ MatchesException,
+ Raises,
)
from testtools.tests.helpers import (
an_exc_info,
@@ -246,10 +248,9 @@
def test_assertRaises_fails_when_different_error_raised(self):
# assertRaises re-raises an exception that it didn't expect.
- self.assertRaises(
- ZeroDivisionError,
- self.assertRaises,
- RuntimeError, self.raiseError, ZeroDivisionError)
+ self.assertThat(lambda: self.assertRaises(RuntimeError,
+ self.raiseError, ZeroDivisionError),
+ Raises(MatchesException(ZeroDivisionError)))
def test_assertRaises_returns_the_raised_exception(self):
# assertRaises returns the exception object that was raised. This is
@@ -606,8 +607,8 @@
def raiseKeyboardInterrupt():
raise KeyboardInterrupt()
self.test.addCleanup(raiseKeyboardInterrupt)
- self.assertRaises(
- KeyboardInterrupt, self.test.run, self.logging_result)
+ self.assertThat(lambda:self.test.run(self.logging_result),
+ Raises(MatchesException(KeyboardInterrupt)))
def test_all_errors_from_MultipleExceptions_reported(self):
# When a MultipleExceptions exception is caught, all the errors are
@@ -935,10 +936,12 @@
"""Tests for skipping of tests functionality."""
def test_skip_causes_skipException(self):
- self.assertRaises(self.skipException, self.skip, "Skip this test")
+ self.assertThat(lambda:self.skip("Skip this test"),
+ Raises(MatchesException(self.skipException)))
def test_can_use_skipTest(self):
- self.assertRaises(self.skipException, self.skipTest, "Skip this test")
+ self.assertThat(lambda:self.skipTest("Skip this test"),
+ Raises(MatchesException(self.skipException)))
def test_skip_without_reason_works(self):
class Test(TestCase):
@@ -1058,7 +1061,8 @@
class Case(TestCase):
def method(self):
self.addOnException(events.index)
- self.assertRaises(ValueError, self.onException, an_exc_info)
+ self.assertThat(lambda: self.onException(an_exc_info),
+ Raises(MatchesException(ValueError)))
case = Case("method")
case.run()
self.assertThat(events, Equals([]))
Follow ups