1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
4 # Maintainer: Jonathan Lange
5 # Author: Robert Collins
8 import StringIO, os, sys
9 from zope.interface import implements
10 from zope.interface.verify import verifyObject
12 from twisted.trial.itrial import IReporter, ITestCase
13 from twisted.trial import unittest, runner, reporter, util
14 from twisted.python import failure, log, reflect, filepath
15 from twisted.python.filepath import FilePath
16 from twisted.scripts import trial
17 from twisted.plugins import twisted_trial
18 from twisted import plugin
19 from twisted.internet import defer
22 pyunit = __import__('unittest')
25 class CapturingDebugger(object):
30 def runcall(self, *args, **kwargs):
31 self._calls.append('runcall')
32 args[0](*args[1:], **kwargs)
36 class CapturingReporter(object):
38 Reporter that keeps a log of all actions performed on it.
49 def __init__(self, stream=None, tbformat=None, rterrors=None,
52 Create a capturing reporter.
55 self.shouldStop = False
57 self._tbformat = tbformat
58 self._rterrors = rterrors
59 self._publisher = publisher
62 def startTest(self, method):
64 Report the beginning of a run of a single test method
65 @param method: an object that is adaptable to ITestMethod
67 self._calls.append('startTest')
70 def stopTest(self, method):
72 Report the status of a single test method
73 @param method: an object that is adaptable to ITestMethod
75 self._calls.append('stopTest')
78 def cleanupErrors(self, errs):
79 """called when the reactor has been left in a 'dirty' state
80 @param errs: a list of L{twisted.python.failure.Failure}s
82 self._calls.append('cleanupError')
85 def addSuccess(self, test):
86 self._calls.append('addSuccess')
91 Do nothing. These tests don't care about done.
96 class TrialRunnerTestsMixin:
98 Mixin defining tests for L{runner.TrialRunner}.
101 self.runner._tearDownLogFile()
104 def test_empty(self):
106 Empty test method, used by the other tests.
110 def _getObservers(self):
111 return log.theLogPublisher.observers
114 def test_addObservers(self):
116 Any log system observers L{TrialRunner.run} adds are removed by the
119 originalCount = len(self._getObservers())
120 self.runner.run(self.test)
121 newCount = len(self._getObservers())
122 self.assertEqual(newCount, originalCount)
125 def test_logFileAlwaysActive(self):
127 Test that a new file is opened on each run.
129 oldSetUpLogFile = self.runner._setUpLogFile
133 l.append(self.runner._logFileObserver)
134 self.runner._setUpLogFile = setUpLogFile
135 self.runner.run(self.test)
136 self.runner.run(self.test)
137 self.assertEqual(len(l), 2)
138 self.failIf(l[0] is l[1], "Should have created a new file observer")
141 def test_logFileGetsClosed(self):
143 Test that file created is closed during the run.
145 oldSetUpLogFile = self.runner._setUpLogFile
149 l.append(self.runner._logFileObject)
150 self.runner._setUpLogFile = setUpLogFile
151 self.runner.run(self.test)
152 self.assertEqual(len(l), 1)
153 self.failUnless(l[0].closed)
157 class TestTrialRunner(TrialRunnerTestsMixin, unittest.TestCase):
159 Tests for L{runner.TrialRunner} with the feature to turn unclean errors
160 into warnings disabled.
163 self.stream = StringIO.StringIO()
164 self.runner = runner.TrialRunner(CapturingReporter, stream=self.stream)
165 self.test = TestTrialRunner('test_empty')
168 def test_publisher(self):
170 The reporter constructed by L{runner.TrialRunner} is passed
171 L{twisted.python.log} as the value for the C{publisher} parameter.
173 result = self.runner._makeResult()
174 self.assertIdentical(result._publisher, log)
178 class TrialRunnerWithUncleanWarningsReporter(TrialRunnerTestsMixin,
181 Tests for the TrialRunner's interaction with an unclean-error suppressing
186 self.stream = StringIO.StringIO()
187 self.runner = runner.TrialRunner(CapturingReporter, stream=self.stream,
188 uncleanWarnings=True)
189 self.test = TestTrialRunner('test_empty')
193 class DryRunMixin(object):
195 suppress = [util.suppress(
196 category=DeprecationWarning,
197 message="Test visitors deprecated in Twisted 8.0")]
202 self.stream = StringIO.StringIO()
203 self.runner = runner.TrialRunner(CapturingReporter,
204 runner.TrialRunner.DRY_RUN,
206 self.makeTestFixtures()
209 def makeTestFixtures(self):
211 Set C{self.test} and C{self.suite}, where C{self.suite} is an empty
216 def test_empty(self):
218 If there are no tests, the reporter should not receive any events to
221 result = self.runner.run(runner.TestSuite())
222 self.assertEqual(result._calls, [])
225 def test_singleCaseReporting(self):
227 If we are running a single test, check the reporter starts, passes and
228 then stops the test during a dry run.
230 result = self.runner.run(self.test)
231 self.assertEqual(result._calls, ['startTest', 'addSuccess', 'stopTest'])
234 def test_testsNotRun(self):
236 When we are doing a dry run, the tests should not actually be run.
238 self.runner.run(self.test)
239 self.assertEqual(self.log, [])
243 class DryRunTest(DryRunMixin, unittest.TestCase):
245 Check that 'dry run' mode works well with Trial tests.
247 def makeTestFixtures(self):
248 class MockTest(unittest.TestCase):
250 self.log.append('test_foo')
251 self.test = MockTest('test_foo')
252 self.suite = runner.TestSuite()
256 class PyUnitDryRunTest(DryRunMixin, unittest.TestCase):
258 Check that 'dry run' mode works well with stdlib unittest tests.
260 def makeTestFixtures(self):
261 class PyunitCase(pyunit.TestCase):
264 self.test = PyunitCase('test_foo')
265 self.suite = pyunit.TestSuite()
269 class TestRunner(unittest.TestCase):
271 self.config = trial.Options()
272 # whitebox hack a reporter in, because plugins are CACHED and will
273 # only reload if the FILE gets changed.
275 parts = reflect.qual(CapturingReporter).split('.')
276 package = '.'.join(parts[:-1])
278 plugins = [twisted_trial._Reporter(
279 "Test Helper Reporter",
281 description="Utility for unit testing.",
287 # XXX There should really be a general way to hook the plugin system
289 def getPlugins(iface, *a, **kw):
290 self.assertEqual(iface, IReporter)
291 return plugins + list(self.original(iface, *a, **kw))
293 self.original = plugin.getPlugins
294 plugin.getPlugins = getPlugins
296 self.standardReport = ['startTest', 'addSuccess', 'stopTest',
297 'startTest', 'addSuccess', 'stopTest',
298 'startTest', 'addSuccess', 'stopTest',
299 'startTest', 'addSuccess', 'stopTest',
300 'startTest', 'addSuccess', 'stopTest',
301 'startTest', 'addSuccess', 'stopTest',
302 'startTest', 'addSuccess', 'stopTest',
303 'startTest', 'addSuccess', 'stopTest',
304 'startTest', 'addSuccess', 'stopTest',
305 'startTest', 'addSuccess', 'stopTest']
309 plugin.getPlugins = self.original
312 def parseOptions(self, args):
313 self.config.parseOptions(args)
317 r = trial._makeRunner(self.config)
318 r.stream = StringIO.StringIO()
319 # XXX The runner should always take care of cleaning this up itself.
320 # It's not clear why this is necessary. The runner always tears down
322 self.addCleanup(r._tearDownLogFile)
323 # XXX The runner should always take care of cleaning this up itself as
324 # well. It's necessary because TrialRunner._setUpTestdir might raise
325 # an exception preventing Reporter.done from being run, leaving the
326 # observer added by Reporter.__init__ still present in the system.
327 # Something better needs to happen inside
328 # TrialRunner._runWithoutDecoration to remove the need for this cludge.
329 r._log = log.LogPublisher()
333 def test_runner_can_get_reporter(self):
334 self.parseOptions([])
335 result = self.config['reporter']
336 runner = self.getRunner()
337 self.assertEqual(result, runner._makeResult().__class__)
340 def test_runner_get_result(self):
341 self.parseOptions([])
342 runner = self.getRunner()
343 result = runner._makeResult()
344 self.assertEqual(result.__class__, self.config['reporter'])
347 def test_uncleanWarningsOffByDefault(self):
349 By default Trial sets the 'uncleanWarnings' option on the runner to
350 False. This means that dirty reactor errors will be reported as
351 errors. See L{test_reporter.TestDirtyReactor}.
353 self.parseOptions([])
354 runner = self.getRunner()
355 self.assertNotIsInstance(runner._makeResult(),
356 reporter.UncleanWarningsReporterWrapper)
359 def test_getsUncleanWarnings(self):
361 Specifying '--unclean-warnings' on the trial command line will cause
362 reporters to be wrapped in a device which converts unclean errors to
363 warnings. See L{test_reporter.TestDirtyReactor} for implications.
365 self.parseOptions(['--unclean-warnings'])
366 runner = self.getRunner()
367 self.assertIsInstance(runner._makeResult(),
368 reporter.UncleanWarningsReporterWrapper)
371 def test_runner_working_directory(self):
372 self.parseOptions(['--temp-directory', 'some_path'])
373 runner = self.getRunner()
374 self.assertEqual(runner.workingDirectory, 'some_path')
377 def test_concurrentImplicitWorkingDirectory(self):
379 If no working directory is explicitly specified and the default
380 working directory is in use by another runner, L{TrialRunner.run}
381 selects a different default working directory to use.
383 self.parseOptions([])
385 # Make sure we end up with the same working directory after this test
386 # as we had before it.
387 self.addCleanup(os.chdir, os.getcwd())
389 # Make a new directory and change into it. This isolates us from state
390 # that other tests might have dumped into this process's temp
392 runDirectory = FilePath(self.mktemp())
393 runDirectory.makedirs()
394 os.chdir(runDirectory.path)
396 firstRunner = self.getRunner()
397 secondRunner = self.getRunner()
401 class ConcurrentCase(unittest.TestCase):
402 def test_first(self):
404 Start a second test run which will have a default working
405 directory which is the same as the working directory of the
406 test run already in progress.
408 # Change the working directory to the value it had before this
409 # test suite was started.
410 where['concurrent'] = subsequentDirectory = os.getcwd()
411 os.chdir(runDirectory.path)
412 self.addCleanup(os.chdir, subsequentDirectory)
414 secondRunner.run(ConcurrentCase('test_second'))
416 def test_second(self):
418 Record the working directory for later analysis.
420 where['record'] = os.getcwd()
422 result = firstRunner.run(ConcurrentCase('test_first'))
423 bad = result.errors + result.failures
428 'concurrent': runDirectory.child('_trial_temp').path,
429 'record': runDirectory.child('_trial_temp-1').path})
432 def test_concurrentExplicitWorkingDirectory(self):
434 If a working directory which is already in use is explicitly specified,
435 L{TrialRunner.run} raises L{_WorkingDirectoryBusy}.
437 self.parseOptions(['--temp-directory', os.path.abspath(self.mktemp())])
439 initialDirectory = os.getcwd()
440 self.addCleanup(os.chdir, initialDirectory)
442 firstRunner = self.getRunner()
443 secondRunner = self.getRunner()
445 class ConcurrentCase(unittest.TestCase):
446 def test_concurrent(self):
448 Try to start another runner in the same working directory and
449 assert that it raises L{_WorkingDirectoryBusy}.
452 util._WorkingDirectoryBusy,
453 secondRunner.run, ConcurrentCase('test_failure'))
455 def test_failure(self):
457 Should not be called, always fails.
459 self.fail("test_failure should never be called.")
461 result = firstRunner.run(ConcurrentCase('test_concurrent'))
462 bad = result.errors + result.failures
467 def test_runner_normal(self):
468 self.parseOptions(['--temp-directory', self.mktemp(),
469 '--reporter', 'capturing',
470 'twisted.trial.test.sample'])
471 my_runner = self.getRunner()
472 loader = runner.TestLoader()
473 suite = loader.loadByName('twisted.trial.test.sample', True)
474 result = my_runner.run(suite)
475 self.assertEqual(self.standardReport, result._calls)
478 def test_runner_debug(self):
479 self.parseOptions(['--reporter', 'capturing',
480 '--debug', 'twisted.trial.test.sample'])
481 my_runner = self.getRunner()
482 debugger = CapturingDebugger()
485 my_runner._getDebugger = get_debugger
486 loader = runner.TestLoader()
487 suite = loader.loadByName('twisted.trial.test.sample', True)
488 result = my_runner.run(suite)
489 self.assertEqual(self.standardReport, result._calls)
490 self.assertEqual(['runcall'], debugger._calls)
494 class RemoveSafelyTests(unittest.TestCase):
496 Tests for L{_removeSafely}.
498 def test_removeSafelyNoTrialMarker(self):
500 If a path doesn't contain a node named C{"_trial_marker"}, that path is
501 not removed by L{runner._removeSafely} and a L{runner._NoTrialMarker}
502 exception is raised instead.
504 directory = self.mktemp()
506 dirPath = filepath.FilePath(directory)
507 self.assertRaises(util._NoTrialMarker, util._removeSafely, dirPath)
510 def test_removeSafelyRemoveFailsMoveSucceeds(self):
512 If an L{OSError} is raised while removing a path in
513 L{runner._removeSafely}, an attempt is made to move the path to a new
518 Raise an C{OSError} to emulate the branch of L{runner._removeSafely}
519 in which path removal fails.
523 # Patch stdout so we can check the print statements in _removeSafely
524 out = StringIO.StringIO()
525 self.patch(sys, 'stdout', out)
527 # Set up a trial directory with a _trial_marker
528 directory = self.mktemp()
530 dirPath = filepath.FilePath(directory)
531 dirPath.child('_trial_marker').touch()
532 # Ensure that path.remove() raises an OSError
533 dirPath.remove = dummyRemove
535 util._removeSafely(dirPath)
536 self.assertIn("could not remove FilePath", out.getvalue())
539 def test_removeSafelyRemoveFailsMoveFails(self):
541 If an L{OSError} is raised while removing a path in
542 L{runner._removeSafely}, an attempt is made to move the path to a new
543 name. If that attempt fails, the L{OSError} is re-raised.
547 Raise an C{OSError} to emulate the branch of L{runner._removeSafely}
548 in which path removal fails.
550 raise OSError("path removal failed")
552 def dummyMoveTo(path):
554 Raise an C{OSError} to emulate the branch of L{runner._removeSafely}
555 in which path movement fails.
557 raise OSError("path movement failed")
559 # Patch stdout so we can check the print statements in _removeSafely
560 out = StringIO.StringIO()
561 self.patch(sys, 'stdout', out)
563 # Set up a trial directory with a _trial_marker
564 directory = self.mktemp()
566 dirPath = filepath.FilePath(directory)
567 dirPath.child('_trial_marker').touch()
569 # Ensure that path.remove() and path.moveTo() both raise OSErrors
570 dirPath.remove = dummyRemove
571 dirPath.moveTo = dummyMoveTo
573 error = self.assertRaises(OSError, util._removeSafely, dirPath)
574 self.assertEqual(str(error), "path movement failed")
575 self.assertIn("could not remove FilePath", out.getvalue())
579 class TestTrialSuite(unittest.TestCase):
581 def test_imports(self):
582 # FIXME, HTF do you test the reactor can be cleaned up ?!!!
583 from twisted.trial.runner import TrialSuite
588 class TestUntilFailure(unittest.TestCase):
589 class FailAfter(unittest.TestCase):
591 A test case that fails when run 3 times in a row.
595 self.count.append(None)
596 if len(self.count) == 3:
597 self.fail('Count reached 3')
601 TestUntilFailure.FailAfter.count = []
602 self.test = TestUntilFailure.FailAfter('test_foo')
603 self.stream = StringIO.StringIO()
604 self.runner = runner.TrialRunner(reporter.Reporter, stream=self.stream)
607 def test_runUntilFailure(self):
609 Test that the runUntilFailure method of the runner actually fail after
612 result = self.runner.runUntilFailure(self.test)
613 self.assertEqual(result.testsRun, 1)
614 self.failIf(result.wasSuccessful())
615 self.assertEqual(self._getFailures(result), 1)
618 def _getFailures(self, result):
620 Get the number of failures that were reported to a result.
622 return len(result.failures)
625 def test_runUntilFailureDecorate(self):
627 C{runUntilFailure} doesn't decorate the tests uselessly: it does it one
628 time when run starts, but not at each turn.
631 def decorate(test, interface):
632 decorated.append((test, interface))
634 self.patch(unittest, "decorate", decorate)
635 result = self.runner.runUntilFailure(self.test)
636 self.assertEqual(result.testsRun, 1)
638 self.assertEqual(len(decorated), 1)
639 self.assertEqual(decorated, [(self.test, ITestCase)])
642 def test_runUntilFailureForceGCDecorate(self):
644 C{runUntilFailure} applies the force-gc decoration after the standard
645 L{ITestCase} decoration, but only one time.
648 def decorate(test, interface):
649 decorated.append((test, interface))
651 self.patch(unittest, "decorate", decorate)
652 self.runner._forceGarbageCollection = True
653 result = self.runner.runUntilFailure(self.test)
654 self.assertEqual(result.testsRun, 1)
656 self.assertEqual(len(decorated), 2)
657 self.assertEqual(decorated,
658 [(self.test, ITestCase),
659 (self.test, unittest._ForceGarbageCollectionDecorator)])
663 class UncleanUntilFailureTests(TestUntilFailure):
665 Test that the run-until-failure feature works correctly with the unclean
670 TestUntilFailure.setUp(self)
671 self.runner = runner.TrialRunner(reporter.Reporter, stream=self.stream,
672 uncleanWarnings=True)
674 def _getFailures(self, result):
676 Get the number of failures that were reported to a result that
677 is wrapped in an UncleanFailureWrapper.
679 return len(result._originalReporter.failures)
683 class BreakingSuite(runner.TestSuite):
685 A L{TestSuite} that logs an error when it is run.
688 def run(self, result):
690 raise RuntimeError("error that occurs outside of a test")
692 log.err(failure.Failure())
696 class TestLoggedErrors(unittest.TestCase):
698 It is possible for an error generated by a test to be logged I{outside} of
699 any test. The log observers constructed by L{TestCase} won't catch these
700 errors. Here we try to generate such errors and ensure they are reported to
701 a L{TestResult} object.
705 self.flushLoggedErrors(RuntimeError)
708 def test_construct(self):
710 Check that we can construct a L{runner.LoggedSuite} and that it
713 suite = runner.LoggedSuite()
714 self.assertEqual(suite.countTestCases(), 0)
717 def test_capturesError(self):
719 Chek that a L{LoggedSuite} reports any logged errors to its result.
721 result = reporter.TestResult()
722 suite = runner.LoggedSuite([BreakingSuite()])
724 self.assertEqual(len(result.errors), 1)
725 self.assertEqual(result.errors[0][0].id(), runner.NOT_IN_TEST)
726 self.failUnless(result.errors[0][1].check(RuntimeError))
730 class TestTestHolder(unittest.TestCase):
733 self.description = "description"
734 self.holder = runner.TestHolder(self.description)
737 def test_holder(self):
739 Check that L{runner.TestHolder} takes a description as a parameter
740 and that this description is returned by the C{id} and
741 C{shortDescription} methods.
743 self.assertEqual(self.holder.id(), self.description)
744 self.assertEqual(self.holder.shortDescription(), self.description)
747 def test_holderImplementsITestCase(self):
749 L{runner.TestHolder} implements L{ITestCase}.
751 self.assertIdentical(self.holder, ITestCase(self.holder))
753 verifyObject(ITestCase, self.holder),
754 "%r claims to provide %r but does not do so correctly."
755 % (self.holder, ITestCase))
758 def test_runsWithStandardResult(self):
760 A L{runner.TestHolder} can run against the standard Python
763 result = pyunit.TestResult()
764 self.holder.run(result)
765 self.assertTrue(result.wasSuccessful())
766 self.assertEqual(1, result.testsRun)
770 class ErrorHolderTestsMixin(object):
772 This mixin defines test methods which can be applied to a
773 L{runner.ErrorHolder} constructed with either a L{Failure} or a
774 C{exc_info}-style tuple.
776 Subclass this and implement C{setUp} to create C{self.holder} referring to a
777 L{runner.ErrorHolder} instance and C{self.error} referring to a L{Failure}
778 which the holder holds.
780 exceptionForTests = ZeroDivisionError('integer division or modulo by zero')
782 class TestResultStub(object):
784 Stub for L{TestResult}.
789 def startTest(self, test):
792 def stopTest(self, test):
795 def addError(self, test, error):
796 self.errors.append((test, error))
799 def test_runsWithStandardResult(self):
801 A L{runner.ErrorHolder} can run against the standard Python
804 result = pyunit.TestResult()
805 self.holder.run(result)
806 self.assertFalse(result.wasSuccessful())
807 self.assertEqual(1, result.testsRun)
812 L{runner.ErrorHolder} adds an error to the result when run.
814 self.holder.run(self.result)
817 [(self.holder, (self.error.type, self.error.value, self.error.tb))])
822 L{runner.ErrorHolder} adds an error to the result when called.
824 self.holder(self.result)
827 [(self.holder, (self.error.type, self.error.value, self.error.tb))])
830 def test_countTestCases(self):
832 L{runner.ErrorHolder.countTestCases} always returns 0.
834 self.assertEqual(self.holder.countTestCases(), 0)
839 L{runner.ErrorHolder.__repr__} returns a string describing the error it
842 self.assertEqual(repr(self.holder),
843 "<ErrorHolder description='description' "
844 "error=ZeroDivisionError('integer division or modulo by zero',)>")
848 class FailureHoldingErrorHolderTests(ErrorHolderTestsMixin, TestTestHolder):
850 Tests for L{runner.ErrorHolder} behaving similarly to L{runner.TestHolder}
851 when constructed with a L{Failure} representing its error.
854 self.description = "description"
855 # make a real Failure so we can construct ErrorHolder()
857 raise self.exceptionForTests
858 except ZeroDivisionError:
859 self.error = failure.Failure()
860 self.holder = runner.ErrorHolder(self.description, self.error)
861 self.result = self.TestResultStub()
865 class ExcInfoHoldingErrorHolderTests(ErrorHolderTestsMixin, TestTestHolder):
867 Tests for L{runner.ErrorHolder} behaving similarly to L{runner.TestHolder}
868 when constructed with a C{exc_info}-style tuple representing its error.
871 self.description = "description"
872 # make a real Failure so we can construct ErrorHolder()
874 raise self.exceptionForTests
875 except ZeroDivisionError:
876 exceptionInfo = sys.exc_info()
877 self.error = failure.Failure()
878 self.holder = runner.ErrorHolder(self.description, exceptionInfo)
879 self.result = self.TestResultStub()
883 class TestMalformedMethod(unittest.TestCase):
885 Test that trial manages when test methods don't have correct signatures.
887 class ContainMalformed(unittest.TestCase):
889 This TestCase holds malformed test methods that trial should handle.
891 def test_foo(self, blah):
895 test_spam = defer.deferredGenerator(test_bar)
897 def _test(self, method):
899 Wrapper for one of the test method of L{ContainMalformed}.
901 stream = StringIO.StringIO()
902 trialRunner = runner.TrialRunner(reporter.Reporter, stream=stream)
903 test = TestMalformedMethod.ContainMalformed(method)
904 result = trialRunner.run(test)
905 self.assertEqual(result.testsRun, 1)
906 self.failIf(result.wasSuccessful())
907 self.assertEqual(len(result.errors), 1)
909 def test_extraArg(self):
911 Test when the method has extra (useless) arguments.
913 self._test('test_foo')
915 def test_noArg(self):
917 Test when the method doesn't have even self as argument.
919 self._test('test_bar')
921 def test_decorated(self):
923 Test a decorated method also fails.
925 self._test('test_spam')
929 class DestructiveTestSuiteTestCase(unittest.TestCase):
931 Test for L{runner.DestructiveTestSuite}.
934 def test_basic(self):
936 Thes destructive test suite should run the tests normally.
939 class MockTest(unittest.TestCase):
942 test = MockTest('test_foo')
943 result = reporter.TestResult()
944 suite = runner.DestructiveTestSuite([test])
945 self.assertEqual(called, [])
947 self.assertEqual(called, [True])
948 self.assertEqual(suite.countTestCases(), 0)
951 def test_shouldStop(self):
953 Test the C{shouldStop} management: raising a C{KeyboardInterrupt} must
957 class MockTest(unittest.TestCase):
961 raise KeyboardInterrupt()
964 result = reporter.TestResult()
965 loader = runner.TestLoader()
966 loader.suiteFactory = runner.DestructiveTestSuite
967 suite = loader.loadClass(MockTest)
968 self.assertEqual(called, [])
970 self.assertEqual(called, [1])
971 # The last test shouldn't have been run
972 self.assertEqual(suite.countTestCases(), 1)
975 def test_cleanup(self):
977 Checks that the test suite cleanups its tests during the run, so that
980 class MockTest(unittest.TestCase):
983 test = MockTest('test_foo')
984 result = reporter.TestResult()
985 suite = runner.DestructiveTestSuite([test])
986 self.assertEqual(suite.countTestCases(), 1)
988 self.assertEqual(suite.countTestCases(), 0)
992 class TestRunnerDeprecation(unittest.TestCase):
994 class FakeReporter(reporter.Reporter):
996 Fake reporter that does *not* implement done() but *does* implement
997 printErrors, separator, printSummary, stream, write and writeln
998 without deprecations.
1005 def printErrors(self, *args):
1008 def printSummary(self, *args):
1011 def write(self, *args):
1014 def writeln(self, *args):
1018 def test_reporterDeprecations(self):
1020 The runner emits a warning if it is using a result that doesn't
1023 trialRunner = runner.TrialRunner(None)
1024 result = self.FakeReporter()
1025 trialRunner._makeResult = lambda: result
1027 # We have to use a pyunit test, otherwise we'll get deprecation
1028 # warnings about using iterate() in a test.
1029 trialRunner.run(pyunit.TestCase('id'))
1032 "%s should implement done() but doesn't. Falling back to "
1033 "printErrors() and friends." % reflect.qual(result.__class__),