diff -r 766570a5d607 Lib/unittest/__init__.py --- a/Lib/unittest/__init__.py Fri Jul 25 08:58:07 2014 -0500 +++ b/Lib/unittest/__init__.py Fri Jul 25 11:14:13 2014 -0400 @@ -47,7 +47,7 @@ __all__ = ['TestResult', 'TestCase', 'TestSuite', 'TextTestRunner', 'TestLoader', 'FunctionTestCase', 'main', 'defaultTestLoader', 'SkipTest', 'skip', 'skipIf', 'skipUnless', - 'expectedFailure', 'TextTestResult', 'installHandler', + 'expectedFailure', 'testBaseClass', 'TextTestResult', 'installHandler', 'registerResult', 'removeResult', 'removeHandler'] # Expose obsolete functions for backwards compatibility @@ -57,7 +57,7 @@ from .result import TestResult from .case import (TestCase, FunctionTestCase, SkipTest, skip, skipIf, - skipUnless, expectedFailure) + skipUnless, expectedFailure, testBaseClass) from .suite import BaseTestSuite, TestSuite from .loader import (TestLoader, defaultTestLoader, makeSuite, getTestCaseNames, findTestCases) diff -r 766570a5d607 Lib/unittest/case.py --- a/Lib/unittest/case.py Fri Jul 25 08:58:07 2014 -0500 +++ b/Lib/unittest/case.py Fri Jul 25 11:14:13 2014 -0400 @@ -119,6 +119,9 @@ test_item.__unittest_expecting_failure__ = True return test_item +def testBaseClass(base_class): + base_class.__unittest_base_class__ = True + return base_class class _BaseTestCaseContext: diff -r 766570a5d607 Lib/unittest/loader.py --- a/Lib/unittest/loader.py Fri Jul 25 08:58:07 2014 -0500 +++ b/Lib/unittest/loader.py Fri Jul 25 11:14:13 2014 -0400 @@ -64,6 +64,8 @@ raise TypeError("Test cases should not be derived from " "TestSuite. Maybe you meant to derive from " "TestCase?") + if testCaseClass.__dict__.get('__unittest_base_class__', False): + raise TypeError("Base test class should not be run.") testCaseNames = self.getTestCaseNames(testCaseClass) if not testCaseNames and hasattr(testCaseClass, 'runTest'): testCaseNames = ['runTest'] @@ -75,7 +77,9 @@ tests = [] for name in dir(module): obj = getattr(module, name) - if isinstance(obj, type) and issubclass(obj, case.TestCase): + if isinstance(obj, type) and \ + issubclass(obj, case.TestCase) and \ + (not obj.__dict__.get('__unittest_base_class__', False)): tests.append(self.loadTestsFromTestCase(obj)) load_tests = getattr(module, 'load_tests', None) diff -r 766570a5d607 Lib/unittest/test/baseclassdecorator.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/unittest/test/baseclassdecorator.py Fri Jul 25 11:14:13 2014 -0400 @@ -0,0 +1,20 @@ +import unittest + +@unittest.testBaseClass +class TestBaseDecoratorBase(unittest.TestCase): + def setUp(self): + self.is_base_class = True + + def test_base_class_should_not_run(self): + """ + This test will fail if run by the base class but will pass + if run by the child class. + """ + self.assertFalse(self.is_base_class) + +class TestBaseDecoratorChild(TestBaseDecoratorBase): + def setUp(self): + self.is_base_class = False + +if __name__ == "__main__": + unittest.main() diff -r 766570a5d607 Lib/unittest/test/test_case.py --- a/Lib/unittest/test/test_case.py Fri Jul 25 08:58:07 2014 -0500 +++ b/Lib/unittest/test/test_case.py Fri Jul 25 11:14:13 2014 -0400 @@ -1569,6 +1569,17 @@ testcase.run() self.assertEqual(MyException.ninstance, 0) + def test_base_class_should_not_run(self): + # Issue #14534: Add method to mark unittest.TestCases as "do not run" + from unittest.test import baseclassdecorator + + result = unittest.TestResult() + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(baseclassdecorator) + + suite.run(result) + self.assertEqual(result.testsRun, 1) if __name__ == "__main__": unittest.main()