Merge lp:~lifeless/testtools/addSkipped into lp:~testtools-committers/testtools/trunk

Proposed by Robert Collins
Status: Merged
Merged at revision: not available
Proposed branch: lp:~lifeless/testtools/addSkipped
Merge into: lp:~testtools-committers/testtools/trunk
Diff against target: None lines
To merge this branch: bzr merge lp:~lifeless/testtools/addSkipped
Reviewer Review Type Date Requested Status
Jonathan Lange Pending
Review via email: mp+4031@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

Yay.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'MANUAL'
--- MANUAL 2008-10-04 03:51:34 +0000
+++ MANUAL 2009-02-28 04:22:53 +0000
@@ -24,6 +24,21 @@
24 ...24 ...
2525
2626
27TestCase.skip
28~~~~~~~~~~~~~
29
30``skip`` is a simple way to have a test stop running and be reported as a
31skipped test, rather than a success/error/failure. This is an altnerative to
32convoluted logic during test loading, permitting later and more localised
33decisions about the appropriateness of running a test. Many reasons exist to
34skip a test - for instance when a dependency is missing, or if the test is
35expensive and should not be run while on laptop battery power, or if the test
36is testing an incomplete feature (this is sometimes called a TODO). Using this
37feature when running your test suite with a TestResult object that is missing
38the ``addSkip`` method will result in the ``addError`` method being invoked
39instead.
40
41
27New assertion methods42New assertion methods
28~~~~~~~~~~~~~~~~~~~~~43~~~~~~~~~~~~~~~~~~~~~
2944
@@ -60,6 +75,15 @@
6075
61``testtools.clone_test_with_new_id`` is a function to copy a test case76``testtools.clone_test_with_new_id`` is a function to copy a test case
62instance to one with a new name. This is helpful for implementing test77instance to one with a new name. This is helpful for implementing test
63paramterisation.78parameterisation.
6479
6580
81Extensions to TestResult
82------------------------
83
84TestResult.addSkip
85~~~~~~~~~~~~~~~~~~
86
87This method is called on result objects when a test skips. The
88``testtools.TestResult`` class records skips in its ``skip_reasons`` instance
89dict. The can be reported on in much the same way as succesful tests.
6690
=== modified file 'testtools/testcase.py'
--- testtools/testcase.py 2008-08-04 05:19:24 +0000
+++ testtools/testcase.py 2009-02-28 04:22:53 +0000
@@ -12,9 +12,15 @@
12import unittest12import unittest
1313
1414
15class TestSkipped(Exception):
16 """Raised within TestCase.run() when a test is skipped."""
17
18
15class TestCase(unittest.TestCase):19class TestCase(unittest.TestCase):
16 """Extensions to the basic TestCase."""20 """Extensions to the basic TestCase."""
1721
22 skipException = TestSkipped
23
18 def __init__(self, *args, **kwargs):24 def __init__(self, *args, **kwargs):
19 unittest.TestCase.__init__(self, *args, **kwargs)25 unittest.TestCase.__init__(self, *args, **kwargs)
20 self._cleanups = []26 self._cleanups = []
@@ -23,6 +29,19 @@
23 def shortDescription(self):29 def shortDescription(self):
24 return self.id()30 return self.id()
2531
32 def skip(self, reason):
33 """Cause this test to be skipped.
34
35 This raises self.skipException(reason). skipException is raised
36 to permit a skip to be triggered at any point(during setUp or the
37 testMethod itself). The run() method catches skipException and
38 translates that into a call to the result objects addSkip method.
39
40 :param reason: The reason why the test is being skipped. This must
41 support being cast into a unicode string for reporting.
42 """
43 raise self.skipException(reason)
44
26 def _formatTypes(self, classOrIterable):45 def _formatTypes(self, classOrIterable):
27 """Format a class or a bunch of classes for display in an error."""46 """Format a class or a bunch of classes for display in an error."""
28 className = getattr(classOrIterable, '__name__', None)47 className = getattr(classOrIterable, '__name__', None)
@@ -116,6 +135,18 @@
116 def runTest(self):135 def runTest(self):
117 """Define this so we can construct a null test object."""136 """Define this so we can construct a null test object."""
118137
138 def _handle_skip(self, result, reason):
139 """Pass a skip to result.
140
141 If result has an addSkip method, this is called. If not, addError is
142 called instead.
143 """
144 addSkip = getattr(result, 'addSkip', None)
145 if not callable(addSkip):
146 result.addError(self, self._exc_info())
147 else:
148 addSkip(self, reason)
149
119 def run(self, result=None):150 def run(self, result=None):
120 if result is None:151 if result is None:
121 result = self.defaultTestResult()152 result = self.defaultTestResult()
@@ -126,6 +157,10 @@
126 self.setUp()157 self.setUp()
127 except KeyboardInterrupt:158 except KeyboardInterrupt:
128 raise159 raise
160 except self.skipException, e:
161 self._handle_skip(result, e.args[0])
162 self._runCleanups(result)
163 return
129 except:164 except:
130 result.addError(self, self._exc_info())165 result.addError(self, self._exc_info())
131 self._runCleanups(result)166 self._runCleanups(result)
@@ -135,6 +170,8 @@
135 try:170 try:
136 testMethod()171 testMethod()
137 ok = True172 ok = True
173 except self.skipException, e:
174 self._handle_skip(result, e.args[0])
138 except self.failureException:175 except self.failureException:
139 result.addFailure(self, self._exc_info())176 result.addFailure(self, self._exc_info())
140 except KeyboardInterrupt:177 except KeyboardInterrupt:
141178
=== modified file 'testtools/testresult.py'
--- testtools/testresult.py 2008-10-04 03:59:24 +0000
+++ testtools/testresult.py 2009-02-28 04:22:53 +0000
@@ -12,6 +12,30 @@
1212
1313
14class TestResult(unittest.TestResult):14class TestResult(unittest.TestResult):
15 """Subclass of unittest.TestResult extending the protocol for flexability.
16
17 :ivar skip_reasons: A dict of skip-reasons -> list of tests. See addSkip.
18 """
19
20 def __init__(self):
21 super(TestResult, self).__init__()
22 self.skip_reasons = {}
23
24 def addSkip(self, test, reason):
25 """Called when a test has been skipped rather than running.
26
27 Like with addSuccess and addError, testStopped should still be called.
28 addSkip is a separate method to addError for clarity, and to keep
29 separate the internal details of how a given TestCase signals a skip,
30 from how unexpected exceptions and failures are signalled.
31
32 :param test: The test that has been skipped.
33 :param reason: The reason for the test being skipped. For instance,
34 u"pyGL is not available".
35 :return: None
36 """
37 skip_list = self.skip_reasons.setdefault(reason, [])
38 skip_list.append(test)
1539
16 def done(self):40 def done(self):
17 """Called when the test runner is done."""41 """Called when the test runner is done."""
@@ -40,6 +64,9 @@
40 def addFailure(self, test, failure):64 def addFailure(self, test, failure):
41 self._dispatch('addFailure', test, failure)65 self._dispatch('addFailure', test, failure)
4266
67 def addSkip(self, test, reason):
68 self._dispatch('addSkip', test, reason)
69
43 def addSuccess(self, test):70 def addSuccess(self, test):
44 self._dispatch('addSuccess', test)71 self._dispatch('addSuccess', test)
4572
4673
=== modified file 'testtools/tests/helpers.py'
--- testtools/tests/helpers.py 2008-10-04 03:51:34 +0000
+++ testtools/tests/helpers.py 2009-02-28 04:22:53 +0000
@@ -33,6 +33,10 @@
33 self._events.append(('addError', test, error))33 self._events.append(('addError', test, error))
34 super(LoggingResult, self).addError(test, error)34 super(LoggingResult, self).addError(test, error)
3535
36 def addSkip(self, test, reason):
37 self._events.append(('addSkip', test, reason))
38 super(LoggingResult, self).addSkip(test, reason)
39
36 def addSuccess(self, test):40 def addSuccess(self, test):
37 self._events.append(('addSuccess', test))41 self._events.append(('addSuccess', test))
38 super(LoggingResult, self).addSuccess(test)42 super(LoggingResult, self).addSuccess(test)
3943
=== modified file 'testtools/tests/test_testresult.py'
--- testtools/tests/test_testresult.py 2008-10-04 03:59:24 +0000
+++ testtools/tests/test_testresult.py 2009-02-28 04:22:53 +0000
@@ -10,6 +10,27 @@
10from testtools.tests.helpers import LoggingResult10from testtools.tests.helpers import LoggingResult
1111
1212
13class TestTestResultContract(TestCase):
14 """Tests for the contract of TestResult's."""
15
16 def test_addSkipped(self):
17 # Calling addSkip(test, reason) completes ok.
18 result = self.makeResult()
19 result.addSkip(self, u"Skipped for some reason")
20
21
22class TestTestResultContract(TestTestResultContract):
23
24 def makeResult(self):
25 return TestResult()
26
27
28class TestMultiTestresultContract(TestTestResultContract):
29
30 def makeResult(self):
31 return MultiTestResult(TestResult(), TestResult())
32
33
13class TestTestResult(TestCase):34class TestTestResult(TestCase):
14 """Tests for `TestResult`."""35 """Tests for `TestResult`."""
1536
@@ -17,6 +38,21 @@
17 """Make an arbitrary result for testing."""38 """Make an arbitrary result for testing."""
18 return TestResult()39 return TestResult()
1940
41 def test_addSkipped(self):
42 # Calling addSkip on a TestResult records the test that was skipped in
43 # its skip_reasons dict.
44 result = self.makeResult()
45 result.addSkip(self, u"Skipped for some reason")
46 self.assertEqual({u"Skipped for some reason":[self]},
47 result.skip_reasons)
48 result.addSkip(self, u"Skipped for some reason")
49 self.assertEqual({u"Skipped for some reason":[self, self]},
50 result.skip_reasons)
51 result.addSkip(self, u"Skipped for another reason")
52 self.assertEqual({u"Skipped for some reason":[self, self],
53 u"Skipped for another reason":[self]},
54 result.skip_reasons)
55
20 def test_done(self):56 def test_done(self):
21 # `TestResult` has a `done` method that, by default, does nothing.57 # `TestResult` has a `done` method that, by default, does nothing.
22 self.makeResult().done()58 self.makeResult().done()
@@ -58,6 +94,13 @@
58 self.multiResult.stopTest(self)94 self.multiResult.stopTest(self)
59 self.assertResultLogsEqual([('stopTest', self)])95 self.assertResultLogsEqual([('stopTest', self)])
6096
97 def test_addSkipped(self):
98 # Calling `addSkip` on a `MultiTestResult` calls addSkip on its
99 # results.
100 reason = u"Skipped for some reason"
101 self.multiResult.addSkip(self, reason)
102 self.assertResultLogsEqual([('addSkip', self, reason)])
103
61 def test_addSuccess(self):104 def test_addSuccess(self):
62 # Calling `addSuccess` on a `MultiTestResult` calls `addSuccess` on105 # Calling `addSuccess` on a `MultiTestResult` calls `addSuccess` on
63 # all its `TestResult`s.106 # all its `TestResult`s.
64107
=== modified file 'testtools/tests/test_testtools.py'
--- testtools/tests/test_testtools.py 2008-10-04 03:51:34 +0000
+++ testtools/tests/test_testtools.py 2009-02-28 04:22:53 +0000
@@ -2,6 +2,7 @@
22
3"""Tests for extensions to the base test library."""3"""Tests for extensions to the base test library."""
44
5import unittest
5from testtools import TestCase, clone_test_with_new_id6from testtools import TestCase, clone_test_with_new_id
6from testtools.tests.helpers import LoggingResult7from testtools.tests.helpers import LoggingResult
78
@@ -375,6 +376,59 @@
375 "the original test instance should be unchanged.")376 "the original test instance should be unchanged.")
376377
377378
379class TestSkipping(TestCase):
380 """Tests for skipping of tests functionality."""
381
382 def test_skip_causes_skipException(self):
383 self.assertRaises(self.skipException, self.skip, "Skip this test")
384
385 def test_skipException_in_setup_calls_result_addSkip(self):
386 class TestThatRaisesInSetUp(TestCase):
387 def setUp(self):
388 self.skip("skipping this test")
389 def test_that_passes(self):
390 pass
391 calls = []
392 result = LoggingResult(calls)
393 test = TestThatRaisesInSetUp("test_that_passes")
394 test.run(result)
395 self.assertEqual([('startTest', test),
396 ('addSkip', test, "skipping this test"), ('stopTest', test)],
397 calls)
398
399 def test_skipException_in_test_method_calls_result_addSkip(self):
400 class SkippingTest(TestCase):
401 def test_that_raises_skipException(self):
402 self.skip("skipping this test")
403 calls = []
404 result = LoggingResult(calls)
405 test = SkippingTest("test_that_raises_skipException")
406 test.run(result)
407 self.assertEqual([('startTest', test),
408 ('addSkip', test, "skipping this test"), ('stopTest', test)],
409 calls)
410
411 def test_skip__in_setup_with_old_result_object_calls_addError(self):
412 class SkippingTest(TestCase):
413 def setUp(self):
414 raise self.skipException("skipping this test")
415 def test_that_raises_skipException(self):
416 pass
417 result = unittest.TestResult()
418 test = SkippingTest("test_that_raises_skipException")
419 test.run(result)
420 self.assertEqual(1, len(result.errors))
421
422 def test_skip_with_old_result_object_calls_addError(self):
423 class SkippingTest(TestCase):
424 def test_that_raises_skipException(self):
425 raise self.skipException("skipping this test")
426 result = unittest.TestResult()
427 test = SkippingTest("test_that_raises_skipException")
428 test.run(result)
429 self.assertEqual(1, len(result.errors))
430
431
378def test_suite():432def test_suite():
379 from unittest import TestLoader433 from unittest import TestLoader
380 return TestLoader().loadTestsFromName(__name__)434 return TestLoader().loadTestsFromName(__name__)

Subscribers

People subscribed via source and target branches