unittest test skipping - Code Review (original) (raw)

OLD

NEW

1 #!/usr/bin/env python

1 #!/usr/bin/env python

2 '''

2 '''

3 Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's

3 Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's

4 Smalltalk testing framework.

4 Smalltalk testing framework.

5

5

6 This module contains the core framework classes that form the basis of

6 This module contains the core framework classes that form the basis of

7 specific test cases and suites (TestCase, TestSuite etc.), and also a

7 specific test cases and suites (TestCase, TestSuite etc.), and also a

8 text-based utility class for running the tests and reporting the results

8 text-based utility class for running the tests and reporting the results

9 (TextTestRunner).

9 (TextTestRunner).

10

10

(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading...

46

46

47 __author__ = "Steve Purcell"

47 __author__ = "Steve Purcell"

48 __email__ = "stephen_purcell at yahoo dot com"

48 __email__ = "stephen_purcell at yahoo dot com"

49 __version__ = "#Revision: 1.63 $"[11:-2]

49 __version__ = "#Revision: 1.63 $"[11:-2]

50

50

51 import time

51 import time

52 import sys

52 import sys

53 import traceback

53 import traceback

54 import os

54 import os

55 import types

55 import types

56 import functools

56

57

57 ##############################################################################

58 ##############################################################################

58 # Exported classes and functions

59 # Exported classes and functions

59 ##############################################################################

60 ##############################################################################

60 __all__ = ['TestResult', 'TestCase', 'TestSuite', 'TextTestRunner',

61 __all__ = ['TestResult', 'TestCase', 'TestSuite', 'TextTestRunner',

61 'TestLoader', 'FunctionTestCase', 'main', 'defaultTestLoader']

62 'TestLoader', 'FunctionTestCase', 'main', 'defaultTestLoader']

62

63

63 # Expose obsolete functions for backwards compatibility

64 # Expose obsolete functions for backwards compatibility

64 __all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases'])

65 __all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases'])

65

66

(...skipping 11 matching lines...) Expand all Loading...

77 return mycmp(self.obj, other.obj) == -1

78 return mycmp(self.obj, other.obj) == -1

78 return K

79 return K

79

80

80 ##############################################################################

81 ##############################################################################

81 # Test framework core

82 # Test framework core

82 ##############################################################################

83 ##############################################################################

83

84

84 def _strclass(cls):

85 def _strclass(cls):

85 return "%s.%s" % (cls.__module__, cls.__name__)

86 return "%s.%s" % (cls.__module__, cls.__name__)

86

87

88

89 class SkipTest(Exception):

90 """

91 Raise this exception in a test to skip it.

92

93 Usually you can use TestResult.skip() or one of the skipping decorators

94 instead of raising this directly.

95 """

96 pass

97

98 class _ExpectedFailure(SkipTest):

99 """

100 Raise this when a test is expected to fail.

101

102 This is an implementation detail.

103 """

104

105 def __init__(self, exc_info):

106 super(_ExpectedFailure, self).__init__()

107 self.exc_info = exc_info

108

109 def _id(obj):

110 return obj

111

112 def skip(reason):

113 """

114 Unconditionally skip a test.

115 """

116 def decorator(test_item):

117 if isinstance(test_item, type) and issubclass(test_item, TestCase):

118 test_item.__unittest_skip__ = True

119 test_item.__unittest_skip_why__ = reason

120 return test_item

121 @functools.wraps(test_item)

122 def skip_wrapper(*args, **kwargs):

123 raise SkipTest(reason)

124 return skip_wrapper

125 return decorator

126

127 def skipIf(condition, reason):

128 """

129 Skip a test if the the condition is true.

130 """

131 if condition:

132 return skip(reason)

133 return _id

134

135 def skipUnless(condition, reason):

136 """

137 Skip a test unless the condition is true.

138 """

139 if not condition:

140 return skip(reason)

141 return _id

142

143

144 _unexpected_success = object()

145

146 def expectedFailure(func):

147 @functools.wraps(func)

148 def wrapper(*args, **kwargs):

149 try:

150 func(*args, **kwargs)

151 except Exception:

152 raise _ExpectedFailure(sys.exc_info())

153 return _unexpected_success

154 return wrapper

155

156

87 __unittest = 1

157 __unittest = 1

88

158

89 class TestResult(object):

159 class TestResult(object):

90 """Holder for test result information.

160 """Holder for test result information.

91

161

92 Test results are automatically managed by the TestCase and TestSuite

162 Test results are automatically managed by the TestCase and TestSuite

93 classes, and do not need to be explicitly manipulated by writers of tests.

163 classes, and do not need to be explicitly manipulated by writers of tests.

94

164

95 Each instance holds the total number of tests run, and collections of

165 Each instance holds the total number of tests run, and collections of

96 failures and errors that occurred among those test runs. The collections

166 failures and errors that occurred among those test runs. The collections

97 contain tuples of (testcase, exceptioninfo), where exceptioninfo is the

167 contain tuples of (testcase, exceptioninfo), where exceptioninfo is the

98 formatted traceback of the error that occurred.

168 formatted traceback of the error that occurred.

99 """

169 """

100 def __init__(self):

170 def __init__(self):

101 self.failures = []

171 self.failures = []

102 self.errors = []

172 self.errors = []

103 self.testsRun = 0

173 self.testsRun = 0

174 self.skipped = []

175 self.expected_failures = []

176 self.unexpected_successes = []

104 self.shouldStop = False

177 self.shouldStop = False

105

178

106 def startTest(self, test):

179 def startTest(self, test):

107 "Called when the given test is about to be run"

180 "Called when the given test is about to be run"

108 self.testsRun = self.testsRun + 1

181 self.testsRun = self.testsRun + 1

109

182

110 def stopTest(self, test):

183 def stopTest(self, test):

111 "Called when the given test has been run"

184 "Called when the given test has been run"

112 pass

185 pass

113

186

114 def addError(self, test, err):

187 def addError(self, test, err):

115 """Called when an error has occurred. 'err' is a tuple of values as

188 """Called when an error has occurred. 'err' is a tuple of values as

116 returned by sys.exc_info().

189 returned by sys.exc_info().

117 """

190 """

118 self.errors.append((test, self._exc_info_to_string(err, test)))

191 self.errors.append((test, self._exc_info_to_string(err, test)))

119

192

120 def addFailure(self, test, err):

193 def addFailure(self, test, err):

121 """Called when an error has occurred. 'err' is a tuple of values as

194 """Called when an error has occurred. 'err' is a tuple of values as

122 returned by sys.exc_info()."""

195 returned by sys.exc_info()."""

123 self.failures.append((test, self._exc_info_to_string(err, test)))

196 self.failures.append((test, self._exc_info_to_string(err, test)))

124

197

125 def addSuccess(self, test):

198 def addSuccess(self, test):

126 "Called when a test has completed successfully"

199 "Called when a test has completed successfully"

127 pass

200 pass

128

201

202 def addSkip(self, test, reason):

203 """Called when a test is skipped."""

204 self.skipped.append((test, reason))

205

206 def addExpectedFailure(self, test, err):

207 """Called when an expected failure/error occured."""

208 self.expected_failures.append(

209 (test, self._exc_info_to_string(err, test)))

210

211 def addUnexpectedSuccess(self, test):

212 """Called when a test was expected to failure, but succeed."""

213 self.unexpected_successes.append(test)

214

129 def wasSuccessful(self):

215 def wasSuccessful(self):

130 "Tells whether or not this result was a success"

216 "Tells whether or not this result was a success"

131 return len(self.failures) == len(self.errors) == 0

217 return len(self.failures) == len(self.errors) == 0

132

218

133 def stop(self):

219 def stop(self):

134 "Indicates that the tests should be aborted"

220 "Indicates that the tests should be aborted"

135 self.shouldStop = True

221 self.shouldStop = True

136

222

137 def _exc_info_to_string(self, err, test):

223 def _exc_info_to_string(self, err, test):

138 """Converts a sys.exc_info()-style tuple of values into a string."""

224 """Converts a sys.exc_info()-style tuple of values into a string."""

(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading...

269

355

270 def run(self, result=None):

356 def run(self, result=None):

271 if result is None: result = self.defaultTestResult()

357 if result is None: result = self.defaultTestResult()

272 result.startTest(self)

358 result.startTest(self)

273 testMethod = getattr(self, self._testMethodName)

359 testMethod = getattr(self, self._testMethodName)

274 try:

360 try:

275 try:

361 try:

276 self.setUp()

362 self.setUp()

277 except Exception:

363 except Exception:

278 result.addError(self, self._exc_info())

364 result.addError(self, self._exc_info())

279 return

365 return

280

366

281 ok = False

367 success = False

282 try:

368 try:

283 testMethod()

369 test_returned = testMethod()

284 ok = True

285 except self.failureException:

370 except self.failureException:

286 result.addFailure(self, self._exc_info())

371 result.addFailure(self, self._exc_info())

372 except _ExpectedFailure as e:

373 result.addExpectedFailure(self, e.exc_info)

374 except SkipTest as e:

375 result.addSkip(self, str(e))

287 except Exception:

376 except Exception:

288 result.addError(self, self._exc_info())

377 result.addError(self, self._exc_info())

378 else:

379 if test_returned is _unexpected_success:

380 result.addUnexpectedSuccess(self)

381 else:

382 success = True

289

383

290 try:

384 try:

291 self.tearDown()

385 self.tearDown()

292 except Exception:

386 except Exception:

293 result.addError(self, self._exc_info())

387 result.addError(self, self._exc_info())

294 ok = False

388 success = False

295 if ok: result.addSuccess(self)

389 if success:

390 result.addSuccess(self)

296 finally:

391 finally:

297 result.stopTest(self)

392 result.stopTest(self)

298

393

299 def __call__(self, *args, **kwds):

394 def __call__(self, *args, **kwds):

300 return self.run(*args, **kwds)

395 return self.run(*args, **kwds)

301

396

302 def debug(self):

397 def debug(self):

303 """Run the test without collecting errors in a TestResult"""

398 """Run the test without collecting errors in a TestResult"""

304 self.setUp()

399 self.setUp()

305 getattr(self, self._testMethodName)()

400 getattr(self, self._testMethodName)()

306 self.tearDown()

401 self.tearDown()

307

402

308 def _exc_info(self):

403 def _exc_info(self):

309 """Return a version of sys.exc_info() with the traceback frame

404 """Return a version of sys.exc_info() with the traceback frame

310 minimised; usually the top level of the traceback frame is not

405 minimised; usually the top level of the traceback frame is not

311 needed.

406 needed.

312 """

407 """

313 return sys.exc_info()

408 return sys.exc_info()

314

409

410 def skip(self, reason):

411 """Skip this test."""

412 raise SkipTest(reason)

413

315 def fail(self, msg=None):

414 def fail(self, msg=None):

316 """Fail immediately, with the given message."""

415 """Fail immediately, with the given message."""

317 raise self.failureException(msg)

416 raise self.failureException(msg)

318

417

319 def failIf(self, expr, msg=None):

418 def failIf(self, expr, msg=None):

320 "Fail the test if the expression is true."

419 "Fail the test if the expression is true."

321 if expr: raise self.failureException(msg)

420 if expr: raise self.failureException(msg)

322

421

323 def failUnless(self, expr, msg=None):

422 def failUnless(self, expr, msg=None):

324 """Fail the test unless the expression is true."""

423 """Fail the test unless the expression is true."""

(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading...

412 def __init__(self, tests=()):

511 def __init__(self, tests=()):

413 self._tests = []

512 self._tests = []

414 self.addTests(tests)

513 self.addTests(tests)

415

514

416 def __repr__(self):

515 def __repr__(self):

417 return "<%s tests=%s>" % (_strclass(self.__class__), self._tests)

516 return "<%s tests=%s>" % (_strclass(self.__class__), self._tests)

418

517

419 __str__ = __repr__

518 __str__ = __repr__

420

519

421 def __eq__(self, other):

520 def __eq__(self, other):

422 if type(self) is not type(other):

521 if not isinstance(other, self.__class__):

423 return False

522 return NotImplemented

424 return self._tests == other._tests

523 return self._tests == other._tests

425

524

426 def __ne__(self, other):

525 def __ne__(self, other):

427 return not self == other

526 return not self == other

428

527

429 # Can't guarantee hash invariant, so flag as unhashable

528 # Can't guarantee hash invariant, so flag as unhashable

430 __hash__ = None

529 __hash__ = None

431

530

432 def __iter__(self):

531 def __iter__(self):

433 return iter(self._tests)

532 return iter(self._tests)

(...skipping 28 matching lines...) Expand all Loading...

462 return result

561 return result

463

562

464 def __call__(self, *args, **kwds):

563 def __call__(self, *args, **kwds):

465 return self.run(*args, **kwds)

564 return self.run(*args, **kwds)

466

565

467 def debug(self):

566 def debug(self):

468 """Run the tests without collecting errors in a TestResult"""

567 """Run the tests without collecting errors in a TestResult"""

469 for test in self._tests: test.debug()

568 for test in self._tests: test.debug()

470

569

471

570

571 class ClassTestSuite(TestSuite):

572 """

573 Suite of tests derived from a single TestCase class.

574 """

575

576 def __init__(self, tests, class_collected_from):

577 super(ClassTestSuite, self).__init__(tests)

578 self.collected_from = class_collected_from

579

580 def id(self):

581 module = getattr(self.collected_from, "__module__", None)

582 if module is not None:

583 return "{0}.{1}".format(module, self.collected_from.__name__)

584 return self.collected_from.__name__

585

586 def run(self, result):

587 if getattr(self.collected_from, "__unittest_skip__", False):

588 # ClassTestSuite result pretends to be a TestCase enough to be

589 # reported.

590 result.startTest(self)

591 try:

592 result.addSkip(self, self.collected_from.__unittest_skip_why__)

593 finally:

594 result.stopTest(self)

595 else:

596 result = super(ClassTestSuite, self).run(result)

597 return result

598

599 shortDescription = id

600

601

472 class FunctionTestCase(TestCase):

602 class FunctionTestCase(TestCase):

473 """A test case that wraps a test function.

603 """A test case that wraps a test function.

474

604

475 This is useful for slipping pre-existing test functions into the

605 This is useful for slipping pre-existing test functions into the

476 unittest framework. Optionally, set-up and tidy-up functions can be

606 unittest framework. Optionally, set-up and tidy-up functions can be

477 supplied. As with TestCase, the tidy-up ('tearDown') function will

607 supplied. As with TestCase, the tidy-up ('tearDown') function will

478 always be called if the set-up ('setUp') function ran successfully.

608 always be called if the set-up ('setUp') function ran successfully.

479 """

609 """

480

610

481 def __init__(self, testFunc, setUp=None, tearDown=None,

611 def __init__(self, testFunc, setUp=None, tearDown=None,

(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading...

533 # Locating and loading tests

663 # Locating and loading tests

534 ##############################################################################

664 ##############################################################################

535

665

536 class TestLoader(object):

666 class TestLoader(object):

537 """This class is responsible for loading tests according to various

667 """This class is responsible for loading tests according to various

538 criteria and returning them wrapped in a TestSuite

668 criteria and returning them wrapped in a TestSuite

539 """

669 """

540 testMethodPrefix = 'test'

670 testMethodPrefix = 'test'

541 sortTestMethodsUsing = cmp

671 sortTestMethodsUsing = cmp

542 suiteClass = TestSuite

672 suiteClass = TestSuite

673 classSuiteClass = ClassTestSuite

543

674

544 def loadTestsFromTestCase(self, testCaseClass):

675 def loadTestsFromTestCase(self, testCaseClass):

545 """Return a suite of all tests cases contained in testCaseClass"""

676 """Return a suite of all tests cases contained in testCaseClass"""

546 if issubclass(testCaseClass, TestSuite):

677 if issubclass(testCaseClass, TestSuite):

547 raise TypeError("Test cases should not be derived from TestSuite. Ma ybe you meant to derive from TestCase?")

678 raise TypeError("Test cases should not be derived from TestSuite. Ma ybe you meant to derive from TestCase?")

548 testCaseNames = self.getTestCaseNames(testCaseClass)

679 testCaseNames = self.getTestCaseNames(testCaseClass)

549 if not testCaseNames and hasattr(testCaseClass, 'runTest'):

680 if not testCaseNames and hasattr(testCaseClass, 'runTest'):

550 testCaseNames = ['runTest']

681 testCaseNames = ['runTest']

551 return self.suiteClass(map(testCaseClass, testCaseNames))

682 suite = self.classSuiteClass(map(testCaseClass, testCaseNames),

683 testCaseClass)

684 return suite

552

685

553 def loadTestsFromModule(self, module):

686 def loadTestsFromModule(self, module):

554 """Return a suite of all tests cases contained in the given module"""

687 """Return a suite of all tests cases contained in the given module"""

555 tests = []

688 tests = []

556 for name in dir(module):

689 for name in dir(module):

557 obj = getattr(module, name)

690 obj = getattr(module, name)

558 if (isinstance(obj, (type, types.ClassType)) and

691 if (isinstance(obj, (type, types.ClassType)) and

559 issubclass(obj, TestCase)):

692 issubclass(obj, TestCase)):

560 tests.append(self.loadTestsFromTestCase(obj))

693 tests.append(self.loadTestsFromTestCase(obj))

561 return self.suiteClass(tests)

694 return self.suiteClass(tests)

(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading...

712 self.stream.flush()

845 self.stream.flush()

713

846

714 def addFailure(self, test, err):

847 def addFailure(self, test, err):

715 TestResult.addFailure(self, test, err)

848 TestResult.addFailure(self, test, err)

716 if self.showAll:

849 if self.showAll:

717 self.stream.writeln("FAIL")

850 self.stream.writeln("FAIL")

718 elif self.dots:

851 elif self.dots:

719 self.stream.write('F')

852 self.stream.write('F')

720 self.stream.flush()

853 self.stream.flush()

721

854

855 def addSkip(self, test, reason):

856 TestResult.addSkip(self, test, reason)

857 if self.showAll:

858 self.stream.writeln("skipped {0!r}".format(reason))

859 elif self.dots:

860 self.stream.write("s")

861 self.stream.flush()

862

863 def addExpectedFailure(self, test, err):

864 TestResult.addExpectedFailure(self, test, err)

865 if self.showAll:

866 self.stream.writeln("expected failure")

867 elif self.dots:

868 self.stream.write(".")

869 self.stream.flush()

870

871 def addUnexpectedSuccess(self, test):

872 TestResult.addUnexpectedSuccess(self, test)

873 if self.showAll:

874 self.stream.writeln("unexpected success")

875 elif self.dots:

876 self.stream.write(".")

877 self.stream.flush()

878

722 def printErrors(self):

879 def printErrors(self):

723 if self.dots or self.showAll:

880 if self.dots or self.showAll:

724 self.stream.writeln()

881 self.stream.writeln()

725 self.printErrorList('ERROR', self.errors)

882 self.printErrorList('ERROR', self.errors)

726 self.printErrorList('FAIL', self.failures)

883 self.printErrorList('FAIL', self.failures)

727

884

728 def printErrorList(self, flavour, errors):

885 def printErrorList(self, flavour, errors):

729 for test, err in errors:

886 for test, err in errors:

730 self.stream.writeln(self.separator1)

887 self.stream.writeln(self.separator1)

731 self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))

888 self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))

(...skipping 21 matching lines...) Expand all Loading...

753 startTime = time.time()

910 startTime = time.time()

754 test(result)

911 test(result)

755 stopTime = time.time()

912 stopTime = time.time()

756 timeTaken = stopTime - startTime

913 timeTaken = stopTime - startTime

757 result.printErrors()

914 result.printErrors()

758 self.stream.writeln(result.separator2)

915 self.stream.writeln(result.separator2)

759 run = result.testsRun

916 run = result.testsRun

760 self.stream.writeln("Ran %d test%s in %.3fs" %

917 self.stream.writeln("Ran %d test%s in %.3fs" %

761 (run, run != 1 and "s" or "", timeTaken))

918 (run, run != 1 and "s" or "", timeTaken))

762 self.stream.writeln()

919 self.stream.writeln()

920 results = map(len, (result.expected_failures,

921 result.unexpected_successes,

922 result.skipped))

923 expected_fails, unexpected_successes, skipped = results

924 infos = []

763 if not result.wasSuccessful():

925 if not result.wasSuccessful():

764 self.stream.write("FAILED (")

926 self.stream.write("FAILED")

765 failed, errored = map(len, (result.failures, result.errors))

927 failed, errored = map(len, (result.failures, result.errors))

766 if failed:

928 if failed:

767 self.stream.write("failures=%d" % failed)

929 infos.append("failures=%d" % failed)

768 if errored:

930 if errored:

769 if failed: self.stream.write(", ")

931 infos.append("errors=%d" % errored)

770 self.stream.write("errors=%d" % errored)

771 self.stream.writeln(")")

772 else:

932 else:

773 self.stream.writeln("OK")

933 self.stream.write("OK")

934 if skipped:

935 infos.append("skipped=%d" % skipped)

936 if expected_fails:

937 infos.append("expected failures=%d" % expected_fails)

938 if unexpected_successes:

939 infos.append("unexpected successes=%d" % unexpected_successes)

940 if infos:

941 self.stream.writeln(" (%s)" % (", ".join(infos),))

774 return result

942 return result

775

943

776

944

777

945

778 ##############################################################################

946 ##############################################################################

779 # Facilities for running tests from the command line

947 # Facilities for running tests from the command line

780 ##############################################################################

948 ##############################################################################

781

949

782 class TestProgram(object):

950 class TestProgram(object):

783 """A command-line program that runs a set of tests; this is primarily

951 """A command-line program that runs a set of tests; this is primarily

(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading...

817 self.parseArgs(argv)

985 self.parseArgs(argv)

818 self.runTests()

986 self.runTests()

819

987

820 def usageExit(self, msg=None):

988 def usageExit(self, msg=None):

821 if msg: print msg

989 if msg: print msg

822 print self.USAGE % self.__dict__

990 print self.USAGE % self.__dict__

823 sys.exit(2)

991 sys.exit(2)

824

992

825 def parseArgs(self, argv):

993 def parseArgs(self, argv):

826 import getopt

994 import getopt

995 long_opts = ['help','verbose','quiet']

827 try:

996 try:

828 options, args = getopt.getopt(argv[1:], 'hHvq',

997 options, args = getopt.getopt(argv[1:], 'hHvq', long_opts)

829 ['help','verbose','quiet'])

830 for opt, value in options:

998 for opt, value in options:

831 if opt in ('-h','-H','--help'):

999 if opt in ('-h','-H','--help'):

832 self.usageExit()

1000 self.usageExit()

833 if opt in ('-q','--quiet'):

1001 if opt in ('-q','--quiet'):

834 self.verbosity = 0

1002 self.verbosity = 0

835 if opt in ('-v','--verbose'):

1003 if opt in ('-v','--verbose'):

836 self.verbosity = 2

1004 self.verbosity = 2

837 if len(args) == 0 and self.defaultTest is None:

1005 if len(args) == 0 and self.defaultTest is None:

838 self.test = self.testLoader.loadTestsFromModule(self.module)

1006 self.test = self.testLoader.loadTestsFromModule(self.module)

839 return

1007 return

(...skipping 24 matching lines...) Expand all Loading...

864

1032

865 main = TestProgram

1033 main = TestProgram

866

1034

867

1035

868 ##############################################################################

1036 ##############################################################################

869 # Executing this module from the command line

1037 # Executing this module from the command line

870 ##############################################################################

1038 ##############################################################################

871

1039

872 if __name__ == "__main__":

1040 if __name__ == "__main__":

873 main(module=None)

1041 main(module=None)

OLD

NEW