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

Proposed by Robert Collins
Status: Merged
Approved by: Jonathan Lange
Approved revision: 249
Merged at revision: 247
Proposed branch: lp:~lifeless/testtools/placeholderskip
Merge into: lp:~testtools-committers/testtools/trunk
Diff against target: 251 lines (+73/-66)
3 files modified
NEWS (+8/-0)
testtools/testcase.py (+37/-40)
testtools/tests/test_testcase.py (+28/-26)
To merge this branch: bzr merge lp:~lifeless/testtools/placeholderskip
Reviewer Review Type Date Requested Status
Jonathan Lange Approve
Review via email: mp+100564@code.launchpad.net

Description of the change

This branch makes it easy to do placeholder skips - something that testscenarios would like to do. (https://code.launchpad.net/~mbp/testscenarios/module-scenarios/+merge/80287)

Placeholder(testid, outcome='addSkip', details={'reason': text_content('module failed to import')}) for instance.

It reduces the total LoC for testtools non-test code and slightly increases the test code size.

I haven't added docs describing the increased functionality beyond the docstring updates. I am happy to do so if appropriate (but I try not to repeat API docs in the manual in other projects).

To post a comment you must log in.
Revision history for this message
Jonathan Lange (jml) wrote :

Thanks Rob, this is a great idea.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS'
--- NEWS 2012-02-16 10:52:15 +0000
+++ NEWS 2012-04-03 07:57:20 +0000
@@ -7,6 +7,14 @@
7NEXT7NEXT
8~~~~8~~~~
99
10Changes
11-------
12
13* ``PlaceHolder`` and ``ErrorHolder`` now support being given result details.
14 (Robert Collins)
15
16* ``ErrorHolder`` is now just a function - all the logic is in ``PlaceHolder``.
17 (Robert Collins)
1018
110.9.14190.9.14
12~~~~~~20~~~~~~
1321
=== modified file 'testtools/testcase.py'
--- testtools/testcase.py 2012-01-29 14:03:59 +0000
+++ testtools/testcase.py 2012-04-03 07:57:20 +0000
@@ -42,7 +42,10 @@
42 )42 )
43from testtools.monkey import patch43from testtools.monkey import patch
44from testtools.runtest import RunTest44from testtools.runtest import RunTest
45from testtools.testresult import TestResult45from testtools.testresult import (
46 ExtendedToOriginalDecorator,
47 TestResult,
48 )
4649
47wraps = try_import('functools.wraps')50wraps = try_import('functools.wraps')
4851
@@ -301,9 +304,7 @@
301 self.__exception_handlers.append(handler)304 self.__exception_handlers.append(handler)
302305
303 def _add_reason(self, reason):306 def _add_reason(self, reason):
304 self.addDetail('reason', content.Content(307 self.addDetail('reason', content.text_content(reason))
305 content.ContentType('text', 'plain'),
306 lambda: [reason.encode('utf8')]))
307308
308 def assertEqual(self, expected, observed, message=''):309 def assertEqual(self, expected, observed, message=''):
309 """Assert that 'expected' is equal to 'observed'.310 """Assert that 'expected' is equal to 'observed'.
@@ -602,21 +603,30 @@
602 particularly suitable for being added to TestResults.603 particularly suitable for being added to TestResults.
603 """604 """
604605
605 def __init__(self, test_id, short_description=None):606 failureException = None
607
608 def __init__(self, test_id, short_description=None, details=None,
609 outcome='addSuccess', error=None):
606 """Construct a `PlaceHolder`.610 """Construct a `PlaceHolder`.
607611
608 :param test_id: The id of the placeholder test.612 :param test_id: The id of the placeholder test.
609 :param short_description: The short description of the place holder613 :param short_description: The short description of the place holder
610 test. If not provided, the id will be used instead.614 test. If not provided, the id will be used instead.
615 :param details: Outcome details as accepted by addSuccess etc.
616 :param outcome: The outcome to call. Defaults to 'addSuccess'.
611 """617 """
612 self._test_id = test_id618 self._test_id = test_id
613 self._short_description = short_description619 self._short_description = short_description
620 self._details = details or {}
621 self._outcome = outcome
622 if error is not None:
623 self._details['traceback'] = content.TracebackContent(error, self)
614624
615 def __call__(self, result=None):625 def __call__(self, result=None):
616 return self.run(result=result)626 return self.run(result=result)
617627
618 def __repr__(self):628 def __repr__(self):
619 internal = [self._test_id]629 internal = [self._outcome, self._test_id, self._details]
620 if self._short_description is not None:630 if self._short_description is not None:
621 internal.append(self._short_description)631 internal.append(self._short_description)
622 return "<%s.%s(%s)>" % (632 return "<%s.%s(%s)>" % (
@@ -636,11 +646,17 @@
636 def id(self):646 def id(self):
637 return self._test_id647 return self._test_id
638648
649 def _result(self, result):
650 if result is None:
651 return TestResult()
652 else:
653 return ExtendedToOriginalDecorator(result)
654
639 def run(self, result=None):655 def run(self, result=None):
640 if result is None:656 result = self._result(result)
641 result = TestResult()
642 result.startTest(self)657 result.startTest(self)
643 result.addSuccess(self)658 outcome = getattr(result, self._outcome)
659 outcome(self, details=self._details)
644 result.stopTest(self)660 result.stopTest(self)
645661
646 def shortDescription(self):662 def shortDescription(self):
@@ -650,37 +666,18 @@
650 return self._short_description666 return self._short_description
651667
652668
653class ErrorHolder(PlaceHolder):669def ErrorHolder(test_id, error, short_description=None, details=None):
654 """A placeholder test that will error out when run."""670 """Construct an `ErrorHolder`.
655671
656 failureException = None672 :param test_id: The id of the test.
657673 :param error: The exc info tuple that will be used as the test's error.
658 def __init__(self, test_id, error, short_description=None):674 This is inserted into the details as 'traceback' - any existing key
659 """Construct an `ErrorHolder`.675 will be overridden.
660676 :param short_description: An optional short description of the test.
661 :param test_id: The id of the test.677 :param details: Outcome details as accepted by addSuccess etc.
662 :param error: The exc info tuple that will be used as the test's error.678 """
663 :param short_description: An optional short description of the test.679 return PlaceHolder(test_id, short_description=short_description,
664 """680 details=details, outcome='addError', error=error)
665 super(ErrorHolder, self).__init__(
666 test_id, short_description=short_description)
667 self._error = error
668
669 def __repr__(self):
670 internal = [self._test_id, self._error]
671 if self._short_description is not None:
672 internal.append(self._short_description)
673 return "<%s.%s(%s)>" % (
674 self.__class__.__module__,
675 self.__class__.__name__,
676 ", ".join(map(repr, internal)))
677
678 def run(self, result=None):
679 if result is None:
680 result = TestResult()
681 result.startTest(self)
682 result.addError(self, self._error)
683 result.stopTest(self)
684681
685682
686# Python 2.4 did not know how to copy functions.683# Python 2.4 did not know how to copy functions.
687684
=== modified file 'testtools/tests/test_testcase.py'
--- testtools/tests/test_testcase.py 2012-02-04 16:44:10 +0000
+++ testtools/tests/test_testcase.py 2012-04-03 07:57:20 +0000
@@ -77,16 +77,21 @@
77 # repr(placeholder) shows you how the object was constructed.77 # repr(placeholder) shows you how the object was constructed.
78 test = PlaceHolder("test id")78 test = PlaceHolder("test id")
79 self.assertEqual(79 self.assertEqual(
80 "<testtools.testcase.PlaceHolder(%s)>" % repr(test.id()),80 "<testtools.testcase.PlaceHolder('addSuccess', %s, {})>" % repr(
81 repr(test))81 test.id()), repr(test))
8282
83 def test_repr_with_description(self):83 def test_repr_with_description(self):
84 # repr(placeholder) shows you how the object was constructed.84 # repr(placeholder) shows you how the object was constructed.
85 test = PlaceHolder("test id", "description")85 test = PlaceHolder("test id", "description")
86 self.assertEqual(86 self.assertEqual(
87 "<testtools.testcase.PlaceHolder(%r, %r)>" % (87 "<testtools.testcase.PlaceHolder('addSuccess', %r, {}, %r)>" % (
88 test.id(), test.shortDescription()),88 test.id(), test.shortDescription()), repr(test))
89 repr(test))89
90 def test_repr_custom_outcome(self):
91 test = PlaceHolder("test id", outcome='addSkip')
92 self.assertEqual(
93 "<testtools.testcase.PlaceHolder('addSkip', %r, {})>" % (
94 test.id()), repr(test))
9095
91 def test_counts_as_one_test(self):96 def test_counts_as_one_test(self):
92 # A placeholder test counts as one test.97 # A placeholder test counts as one test.
@@ -107,6 +112,17 @@
107 [('startTest', test), ('addSuccess', test), ('stopTest', test)],112 [('startTest', test), ('addSuccess', test), ('stopTest', test)],
108 log)113 log)
109114
115 def test_supplies_details(self):
116 details = {'quux':None}
117 test = PlaceHolder('foo', details=details)
118 result = ExtendedTestResult()
119 test.run(result)
120 self.assertEqual(
121 [('startTest', test),
122 ('addSuccess', test, details),
123 ('stopTest', test)],
124 result._events)
125
110 def test_call_is_run(self):126 def test_call_is_run(self):
111 # A PlaceHolder can be called, in which case it behaves like run.127 # A PlaceHolder can be called, in which case it behaves like run.
112 test = self.makePlaceHolder()128 test = self.makePlaceHolder()
@@ -127,6 +143,8 @@
127143
128144
129class TestErrorHolder(TestCase):145class TestErrorHolder(TestCase):
146 # Note that these tests exist because ErrorHolder exists - it could be
147 # deprecated and dropped at this point.
130148
131 run_test_with = FullStackRunTest149 run_test_with = FullStackRunTest
132150
@@ -158,23 +176,6 @@
158 test = ErrorHolder("test id", self.makeException(), "description")176 test = ErrorHolder("test id", self.makeException(), "description")
159 self.assertEqual("description", test.shortDescription())177 self.assertEqual("description", test.shortDescription())
160178
161 def test_repr_just_id(self):
162 # repr(placeholder) shows you how the object was constructed.
163 error = self.makeException()
164 test = ErrorHolder("test id", error)
165 self.assertEqual(
166 "<testtools.testcase.ErrorHolder(%r, %r)>" % (test.id(), error),
167 repr(test))
168
169 def test_repr_with_description(self):
170 # repr(placeholder) shows you how the object was constructed.
171 error = self.makeException()
172 test = ErrorHolder("test id", error, "description")
173 self.assertEqual(
174 "<testtools.testcase.ErrorHolder(%r, %r, %r)>" % (
175 test.id(), error, test.shortDescription()),
176 repr(test))
177
178 def test_counts_as_one_test(self):179 def test_counts_as_one_test(self):
179 # A placeholder test counts as one test.180 # A placeholder test counts as one test.
180 test = self.makePlaceHolder()181 test = self.makePlaceHolder()
@@ -186,14 +187,15 @@
186 self.assertEqual(test.id(), str(test))187 self.assertEqual(test.id(), str(test))
187188
188 def test_runs_as_error(self):189 def test_runs_as_error(self):
189 # When run, a PlaceHolder test records a success.190 # When run, an ErrorHolder test records an error.
190 error = self.makeException()191 error = self.makeException()
191 test = self.makePlaceHolder(error=error)192 test = self.makePlaceHolder(error=error)
192 log = []193 result = ExtendedTestResult()
193 test.run(LoggingResult(log))194 log = result._events
195 test.run(result)
194 self.assertEqual(196 self.assertEqual(
195 [('startTest', test),197 [('startTest', test),
196 ('addError', test, error),198 ('addError', test, test._details),
197 ('stopTest', test)], log)199 ('stopTest', test)], log)
198200
199 def test_call_is_run(self):201 def test_call_is_run(self):

Subscribers

People subscribed via source and target branches