diff options
| author | Zane Bitter <zbitter@redhat.com> | 2018-04-18 15:06:13 -0400 |
|---|---|---|
| committer | Zane Bitter <zbitter@redhat.com> | 2018-11-19 16:41:38 -0500 |
| commit | de9830ec7d87bccbd99920cbcfc9008f9b4d951c (patch) | |
| tree | 5c5d89f7984c329d37beb8976c87a316a2d2f1f8 | |
| parent | 7f9daba0ec01e494d8a283cb23e9e531343244b3 (diff) | |
| download | testtools-de9830ec7d87bccbd99920cbcfc9008f9b4d951c.tar.gz | |
Allow skip/skipIf/skipUnless as class decorators
In unittest/unittest2 the skip/skipIf/skipUnless decorators can be used to
decorate either individual test methods or entire TestCase classes.
However, the testtools equivalents could previously only be used to
decorate test methods. If used on a TestCase the class would be replaced
with a function, resulting in the tests either not be discovered at all or
(if a custom test loader was used) potentially an error.
This change allows the skip decorators to be used on the TestCase subclass,
for equivalent functionality with unittest.
Fixes #205
Signed-off-by: Zane Bitter <zbitter@redhat.com>
| -rw-r--r-- | NEWS | 10 | ||||
| -rw-r--r-- | README.rst | 1 | ||||
| -rw-r--r-- | testtools/runtest.py | 6 | ||||
| -rw-r--r-- | testtools/testcase.py | 16 | ||||
| -rw-r--r-- | testtools/tests/test_testcase.py | 65 |
5 files changed, 92 insertions, 6 deletions
@@ -3,6 +3,16 @@ testtools NEWS Changes and improvements to testtools_, grouped by release. +NEXT +~~~~ + +Improvements +------------ + +* The skip, skipIf, and skipUnless decorators can now be used as class + decorators as well as test method decorators, just as they can in + unittest. + 2.3.0 ~~~~~ @@ -93,3 +93,4 @@ Thanks * ClusterHQ Ltd * Tristan Seligmann * Jonathan Jacobs + * Zane Bitter diff --git a/testtools/runtest.py b/testtools/runtest.py index cb38d78..eecf072 100644 --- a/testtools/runtest.py +++ b/testtools/runtest.py @@ -124,10 +124,12 @@ class RunTest(object): def _run_core(self): """Run the user supplied test code.""" test_method = self.case._get_test_method() - if getattr(test_method, '__unittest_skip__', False): + skip_case = getattr(self.case, '__unittest_skip__', False) + if skip_case or getattr(test_method, '__unittest_skip__', False): self.result.addSkip( self.case, - reason=getattr(test_method, '__unittest_skip_why__', None) + reason=getattr(self.case if skip_case else test_method, + '__unittest_skip_why__', None) ) return diff --git a/testtools/testcase.py b/testtools/testcase.py index 9fce671..2e128f2 100644 --- a/testtools/testcase.py +++ b/testtools/testcase.py @@ -20,6 +20,7 @@ import copy import functools import itertools import sys +import types import warnings from extras import ( @@ -940,6 +941,12 @@ class WithAttributes(object): return orig + '[' + ','.join(sorted(attributes)) + ']' +class_types = [type] +if getattr(types, 'ClassType', None) is not None: + class_types.append(types.ClassType) +class_types = tuple(class_types) + + def skip(reason): """A decorator to skip unit tests. @@ -948,10 +955,11 @@ def skip(reason): @unittest.skip decorator. """ def decorator(test_item): - @functools.wraps(test_item) - def skip_wrapper(*args, **kwargs): - raise TestCase.skipException(reason) - test_item = skip_wrapper + if not isinstance(test_item, class_types): + @functools.wraps(test_item) + def skip_wrapper(*args, **kwargs): + raise TestCase.skipException(reason) + test_item = skip_wrapper # This attribute signals to RunTest._run_core that the entire test # must be skipped - including setUp and tearDown. This makes us diff --git a/testtools/tests/test_testcase.py b/testtools/tests/test_testcase.py index 7076179..56a8152 100644 --- a/testtools/tests/test_testcase.py +++ b/testtools/tests/test_testcase.py @@ -43,6 +43,7 @@ from testtools.testcase import ( attr, Nullary, WithAttributes, + TestSkipped, ) from testtools.testresult.doubles import ( Python26TestResult, @@ -1608,6 +1609,48 @@ class TestSkipping(TestCase): test2.run(result2) self.assertEqual('addFailure', events2[1][0]) + def test_skip_class_decorator(self): + @skip("skipping this testcase") + class SkippingTest(TestCase): + def test_that_is_decorated_with_skip(self): + self.fail() + events = [] + result = Python26TestResult(events) + try: + test = SkippingTest("test_that_is_decorated_with_skip") + except TestSkipped: + self.fail('TestSkipped raised') + test.run(result) + self.assertEqual('addSuccess', events[1][0]) + + def test_skipIf_class_decorator(self): + @skipIf(True, "skipping this testcase") + class SkippingTest(TestCase): + def test_that_is_decorated_with_skipIf(self): + self.fail() + events = [] + result = Python26TestResult(events) + try: + test = SkippingTest("test_that_is_decorated_with_skipIf") + except TestSkipped: + self.fail('TestSkipped raised') + test.run(result) + self.assertEqual('addSuccess', events[1][0]) + + def test_skipUnless_class_decorator(self): + @skipUnless(False, "skipping this testcase") + class SkippingTest(TestCase): + def test_that_is_decorated_with_skipUnless(self): + self.fail() + events = [] + result = Python26TestResult(events) + try: + test = SkippingTest("test_that_is_decorated_with_skipUnless") + except TestSkipped: + self.fail('TestSkipped raised') + test.run(result) + self.assertEqual('addSuccess', events[1][0]) + def check_skip_decorator_does_not_run_setup(self, decorator, reason): class SkippingTest(TestCase): @@ -1623,6 +1666,28 @@ class TestSkipping(TestCase): self.fail() test = SkippingTest('test_skipped') + self.check_test_does_not_run_setup(test, reason) + + # Use the decorator passed to us: + @decorator + class SkippingTestCase(TestCase): + + setup_ran = False + + def setUp(self): + super(SkippingTestCase, self).setUp() + self.setup_ran = True + + def test_skipped(self): + self.fail() + + try: + test = SkippingTestCase('test_skipped') + except TestSkipped: + self.fail('TestSkipped raised') + self.check_test_does_not_run_setup(test, reason) + + def check_test_does_not_run_setup(self, test, reason): result = test.run() self.assertTrue(result.wasSuccessful()) self.assertTrue(reason in result.skip_reasons, result.skip_reasons) |
