3 Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's
4 Smalltalk testing framework.
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
8 text-based utility class for running the tests and reporting the results
15 class IntegerArithmenticTestCase(unittest.TestCase):
16 def testAdd(self): ## test method names begin 'test*'
17 self.assertEquals((1 + 2), 3)
18 self.assertEquals(0 + 1, 1)
19 def testMultiply(self):
20 self.assertEquals((0 * 10), 0)
21 self.assertEquals((5 * 8), 40)
23 if __name__ == '__main__':
26 Further information is available in the bundled documentation, and from
28 http://docs.python.org/lib/module-unittest.html
30 Copyright (c) 1999-2003 Steve Purcell
31 This module is free software, and you may redistribute it and/or modify
32 it under the same terms as Python itself, so long as this copyright message
33 and disclaimer are retained in their original form.
35 IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
36 SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
37 THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
40 THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
41 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
42 PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
43 AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
44 SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
47 __author__
= "Steve Purcell"
48 __email__
= "stephen_purcell at yahoo dot com"
49 __version__
= "#Revision: 1.63 $"[11:-2]
57 ##############################################################################
58 # Exported classes and functions
59 ##############################################################################
60 __all__
= ['TestResult', 'TestCase', 'TestSuite', 'TextTestRunner',
61 'TestLoader', 'FunctionTestCase', 'main', 'defaultTestLoader']
63 # Expose obsolete functions for backwards compatibility
64 __all__
.extend(['getTestCaseNames', 'makeSuite', 'findTestCases'])
67 ##############################################################################
69 ##############################################################################
72 return "%s.%s" % (cls
.__module
__, cls
.__name
__)
77 """Holder for test result information.
79 Test results are automatically managed by the TestCase and TestSuite
80 classes, and do not need to be explicitly manipulated by writers of tests.
82 Each instance holds the total number of tests run, and collections of
83 failures and errors that occurred among those test runs. The collections
84 contain tuples of (testcase, exceptioninfo), where exceptioninfo is the
85 formatted traceback of the error that occurred.
91 self
.shouldStop
= False
93 def startTest(self
, test
):
94 "Called when the given test is about to be run"
95 self
.testsRun
= self
.testsRun
+ 1
97 def stopTest(self
, test
):
98 "Called when the given test has been run"
101 def addError(self
, test
, err
):
102 """Called when an error has occurred. 'err' is a tuple of values as
103 returned by sys.exc_info().
105 self
.errors
.append((test
, self
._exc
_info
_to
_string
(err
, test
)))
107 def addFailure(self
, test
, err
):
108 """Called when an error has occurred. 'err' is a tuple of values as
109 returned by sys.exc_info()."""
110 self
.failures
.append((test
, self
._exc
_info
_to
_string
(err
, test
)))
112 def addSuccess(self
, test
):
113 "Called when a test has completed successfully"
116 def wasSuccessful(self
):
117 "Tells whether or not this result was a success"
118 return len(self
.failures
) == len(self
.errors
) == 0
121 "Indicates that the tests should be aborted"
122 self
.shouldStop
= True
124 def _exc_info_to_string(self
, err
, test
):
125 """Converts a sys.exc_info()-style tuple of values into a string."""
126 exctype
, value
, tb
= err
127 # Skip test runner traceback levels
128 while tb
and self
._is
_relevant
_tb
_level
(tb
):
130 if exctype
is test
.failureException
:
131 # Skip assert*() traceback levels
132 length
= self
._count
_relevant
_tb
_levels
(tb
)
133 return ''.join(traceback
.format_exception(exctype
, value
,
135 return ''.join(traceback
.format_exception(exctype
, value
, tb
))
137 def _is_relevant_tb_level(self
, tb
):
138 return '__unittest' in tb
.tb_frame
.f_globals
140 def _count_relevant_tb_levels(self
, tb
):
142 while tb
and not self
._is
_relevant
_tb
_level
(tb
):
148 return "<%s run=%i errors=%i failures=%i>" % \
149 (_strclass(self
.__class
__), self
.testsRun
, len(self
.errors
),
153 """A class whose instances are single test cases.
155 By default, the test code itself should be placed in a method named
158 If the fixture may be used for many test cases, create as
159 many test methods as are needed. When instantiating such a TestCase
160 subclass, specify in the constructor arguments the name of the test method
161 that the instance is to execute.
163 Test authors should subclass TestCase for their own tests. Construction
164 and deconstruction of the test's environment ('fixture') can be
165 implemented by overriding the 'setUp' and 'tearDown' methods respectively.
167 If it is necessary to override the __init__ method, the base class
168 __init__ method must always be called. It is important that subclasses
169 should not change the signature of their __init__ method, since instances
170 of the classes are instantiated automatically by parts of the framework
174 # This attribute determines which exception will be raised when
175 # the instance's assertion methods fail; test methods raising this
176 # exception will be deemed to have 'failed' rather than 'errored'
178 failureException
= AssertionError
180 def __init__(self
, methodName
='runTest'):
181 """Create an instance of the class that will use the named test
182 method when executed. Raises a ValueError if the instance does
183 not have a method with the specified name.
186 self
._testMethodName
= methodName
187 testMethod
= getattr(self
, methodName
)
188 self
._testMethodDoc
= testMethod
.__doc
__
189 except AttributeError:
190 raise ValueError("no such test method in %s: %s"
191 % (self
.__class
__, methodName
))
194 "Hook method for setting up the test fixture before exercising it."
198 "Hook method for deconstructing the test fixture after testing it."
201 def countTestCases(self
):
204 def defaultTestResult(self
):
207 def shortDescription(self
):
208 """Returns a one-line description of the test, or None if no
209 description has been provided.
211 The default implementation of this method returns the first line of
212 the specified test method's docstring.
214 doc
= self
._testMethodDoc
215 return doc
and doc
.split("\n")[0].strip() or None
218 return "%s.%s" % (_strclass(self
.__class
__), self
._testMethodName
)
220 def __eq__(self
, other
):
221 if type(self
) is not type(other
):
224 return self
._testMethodName
== other
._testMethodName
226 def __ne__(self
, other
):
227 return not self
== other
230 return hash((type(self
), self
._testMethodName
))
233 return "%s (%s)" % (self
._testMethodName
, _strclass(self
.__class
__))
236 return "<%s testMethod=%s>" % \
237 (_strclass(self
.__class
__), self
._testMethodName
)
239 def run(self
, result
=None):
240 if result
is None: result
= self
.defaultTestResult()
241 result
.startTest(self
)
242 testMethod
= getattr(self
, self
._testMethodName
)
246 except KeyboardInterrupt:
249 result
.addError(self
, self
._exc
_info
())
256 except self
.failureException
:
257 result
.addFailure(self
, self
._exc
_info
())
258 except KeyboardInterrupt:
261 result
.addError(self
, self
._exc
_info
())
265 except KeyboardInterrupt:
268 result
.addError(self
, self
._exc
_info
())
270 if ok
: result
.addSuccess(self
)
272 result
.stopTest(self
)
274 def __call__(self
, *args
, **kwds
):
275 return self
.run(*args
, **kwds
)
278 """Run the test without collecting errors in a TestResult"""
280 getattr(self
, self
._testMethodName
)()
284 """Return a version of sys.exc_info() with the traceback frame
285 minimised; usually the top level of the traceback frame is not
288 return sys
.exc_info()
290 def fail(self
, msg
=None):
291 """Fail immediately, with the given message."""
292 raise self
.failureException(msg
)
294 def failIf(self
, expr
, msg
=None):
295 "Fail the test if the expression is true."
296 if expr
: raise self
.failureException(msg
)
298 def failUnless(self
, expr
, msg
=None):
299 """Fail the test unless the expression is true."""
300 if not expr
: raise self
.failureException(msg
)
302 def failUnlessRaises(self
, excClass
, callableObj
, *args
, **kwargs
):
303 """Fail unless an exception of class excClass is thrown
304 by callableObj when invoked with arguments args and keyword
305 arguments kwargs. If a different type of exception is
306 thrown, it will not be caught, and the test case will be
307 deemed to have suffered an error, exactly as for an
308 unexpected exception.
311 callableObj(*args
, **kwargs
)
315 excName
= str(getattr(excClass
, '__name__', excClass
))
316 objName
= str(getattr(callableObj
, '__name__', callableObj
))
317 raise self
.failureException("%s not raised by %s" % (excName
,
320 def failUnlessEqual(self
, first
, second
, msg
=None):
321 """Fail if the two objects are unequal as determined by the '=='
324 if not first
== second
:
325 raise self
.failureException(msg
or '%r != %r' % (first
, second
))
327 def failIfEqual(self
, first
, second
, msg
=None):
328 """Fail if the two objects are equal as determined by the '=='
332 raise self
.failureException(msg
or '%r == %r' % (first
, second
))
334 def failUnlessAlmostEqual(self
, first
, second
, *, places
=7, msg
=None):
335 """Fail if the two objects are unequal as determined by their
336 difference rounded to the given number of decimal places
337 (default 7) and comparing to zero.
339 Note that decimal places (from zero) are usually not the same
340 as significant digits (measured from the most signficant digit).
342 if round(abs(second
-first
), places
) != 0:
343 raise self
.failureException(msg
or '%r != %r within %r places'
344 % (first
, second
, places
))
346 def failIfAlmostEqual(self
, first
, second
, *, places
=7, msg
=None):
347 """Fail if the two objects are equal as determined by their
348 difference rounded to the given number of decimal places
349 (default 7) and comparing to zero.
351 Note that decimal places (from zero) are usually not the same
352 as significant digits (measured from the most signficant digit).
354 if round(abs(second
-first
), places
) == 0:
355 raise self
.failureException(msg
or '%r == %r within %r places'
356 % (first
, second
, places
))
358 # Synonyms for assertion methods
360 assertEqual
= assertEquals
= failUnlessEqual
362 assertNotEqual
= assertNotEquals
= failIfEqual
364 assertAlmostEqual
= assertAlmostEquals
= failUnlessAlmostEqual
366 assertNotAlmostEqual
= assertNotAlmostEquals
= failIfAlmostEqual
368 assertRaises
= failUnlessRaises
370 assert_
= assertTrue
= failUnless
377 """A test suite is a composite test consisting of a number of TestCases.
379 For use, create an instance of TestSuite, then add test case instances.
380 When all tests have been added, the suite can be passed to a test
381 runner, such as TextTestRunner. It will run the individual test cases
382 in the order in which they were added, aggregating the results. When
383 subclassing, do not forget to call the base class constructor.
385 def __init__(self
, tests
=()):
390 return "<%s tests=%s>" % (_strclass(self
.__class
__), self
._tests
)
394 def __eq__(self
, other
):
395 if type(self
) is not type(other
):
397 return self
._tests
== other
._tests
399 def __ne__(self
, other
):
400 return not self
== other
403 return iter(self
._tests
)
405 def countTestCases(self
):
407 for test
in self
._tests
:
408 cases
+= test
.countTestCases()
411 def addTest(self
, test
):
413 if not hasattr(test
, '__call__'):
414 raise TypeError("the test to add must be callable")
415 if isinstance(test
, type) and issubclass(test
, (TestCase
, TestSuite
)):
416 raise TypeError("TestCases and TestSuites must be instantiated "
417 "before passing them to addTest()")
418 self
._tests
.append(test
)
420 def addTests(self
, tests
):
421 if isinstance(tests
, str):
422 raise TypeError("tests must be an iterable of tests, not a string")
426 def run(self
, result
):
427 for test
in self
._tests
:
428 if result
.shouldStop
:
433 def __call__(self
, *args
, **kwds
):
434 return self
.run(*args
, **kwds
)
437 """Run the tests without collecting errors in a TestResult"""
438 for test
in self
._tests
: test
.debug()
441 class FunctionTestCase(TestCase
):
442 """A test case that wraps a test function.
444 This is useful for slipping pre-existing test functions into the
445 unittest framework. Optionally, set-up and tidy-up functions can be
446 supplied. As with TestCase, the tidy-up ('tearDown') function will
447 always be called if the set-up ('setUp') function ran successfully.
450 def __init__(self
, testFunc
, setUp
=None, tearDown
=None,
452 TestCase
.__init
__(self
)
453 self
.__setUpFunc
= setUp
454 self
.__tearDownFunc
= tearDown
455 self
.__testFunc
= testFunc
456 self
.__description
= description
459 if self
.__setUpFunc
is not None:
463 if self
.__tearDownFunc
is not None:
464 self
.__tearDownFunc
()
470 return self
.__testFunc
.__name
__
472 def __eq__(self
, other
):
473 if type(self
) is not type(other
):
476 return self
.__setUpFunc
== other
.__setUpFunc
and \
477 self
.__tearDownFunc
== other
.__tearDownFunc
and \
478 self
.__testFunc
== other
.__testFunc
and \
479 self
.__description
== other
.__description
481 def __ne__(self
, other
):
482 return not self
== other
485 return hash((type(self
), self
.__setUpFunc
, self
.__tearDownFunc
,
486 self
.__testFunc
, self
.__description
))
489 return "%s (%s)" % (_strclass(self
.__class
__),
490 self
.__testFunc
.__name
__)
493 return "<%s testFunc=%s>" % (_strclass(self
.__class
__),
496 def shortDescription(self
):
497 if self
.__description
is not None: return self
.__description
498 doc
= self
.__testFunc
.__doc
__
499 return doc
and doc
.split("\n")[0].strip() or None
503 ##############################################################################
504 # Locating and loading tests
505 ##############################################################################
508 'Convert a cmp= function into a key= function'
510 def __init__(self
, obj
, *args
):
512 def __lt__(self
, other
):
513 return mycmp(self
.obj
, other
.obj
) == -1
517 """This class is responsible for loading tests according to various
518 criteria and returning them wrapped in a TestSuite
520 testMethodPrefix
= 'test'
521 sortTestMethodsUsing
= cmp
522 suiteClass
= TestSuite
524 def loadTestsFromTestCase(self
, testCaseClass
):
525 """Return a suite of all tests cases contained in testCaseClass"""
526 if issubclass(testCaseClass
, TestSuite
):
527 raise TypeError("Test cases should not be derived from TestSuite."
528 "Maybe you meant to derive from TestCase?")
529 testCaseNames
= self
.getTestCaseNames(testCaseClass
)
530 if not testCaseNames
and hasattr(testCaseClass
, 'runTest'):
531 testCaseNames
= ['runTest']
532 return self
.suiteClass(map(testCaseClass
, testCaseNames
))
534 def loadTestsFromModule(self
, module
):
535 """Return a suite of all tests cases contained in the given module"""
537 for name
in dir(module
):
538 obj
= getattr(module
, name
)
539 if isinstance(obj
, type) and issubclass(obj
, TestCase
):
540 tests
.append(self
.loadTestsFromTestCase(obj
))
541 return self
.suiteClass(tests
)
543 def loadTestsFromName(self
, name
, module
=None):
544 """Return a suite of all tests cases given a string specifier.
546 The name may resolve either to a module, a test case class, a
547 test method within a test case class, or a callable object which
548 returns a TestCase or TestSuite instance.
550 The method optionally resolves the names relative to a given module.
552 parts
= name
.split('.')
554 parts_copy
= parts
[:]
557 module
= __import__('.'.join(parts_copy
))
561 if not parts_copy
: raise
565 parent
, obj
= obj
, getattr(obj
, part
)
567 if type(obj
) == types
.ModuleType
:
568 return self
.loadTestsFromModule(obj
)
569 elif isinstance(obj
, type) and issubclass(obj
, TestCase
):
570 return self
.loadTestsFromTestCase(obj
)
571 elif (isinstance(obj
, types
.FunctionType
) and
572 isinstance(parent
, type) and
573 issubclass(parent
, TestCase
)):
576 # static methods follow a different path
577 if not isinstance(getattr(inst
, name
), types
.FunctionType
):
578 return TestSuite([inst
])
579 elif isinstance(obj
, TestSuite
):
582 if hasattr(obj
, '__call__'):
584 if isinstance(test
, TestSuite
):
586 elif isinstance(test
, TestCase
):
587 return TestSuite([test
])
589 raise TypeError("calling %s returned %s, not a test" %
592 raise TypeError("don't know how to make test from: %s" % obj
)
594 def loadTestsFromNames(self
, names
, module
=None):
595 """Return a suite of all tests cases found using the given sequence
596 of string specifiers. See 'loadTestsFromName()'.
598 suites
= [self
.loadTestsFromName(name
, module
) for name
in names
]
599 return self
.suiteClass(suites
)
601 def getTestCaseNames(self
, testCaseClass
):
602 """Return a sorted sequence of method names found within testCaseClass
604 def isTestMethod(attrname
, testCaseClass
=testCaseClass
,
605 prefix
=self
.testMethodPrefix
):
606 return attrname
.startswith(prefix
) \
607 and hasattr(getattr(testCaseClass
, attrname
), '__call__')
608 testFnNames
= list(filter(isTestMethod
, dir(testCaseClass
)))
609 if self
.sortTestMethodsUsing
:
610 testFnNames
.sort(key
=CmpToKey(self
.sortTestMethodsUsing
))
615 defaultTestLoader
= TestLoader()
618 ##############################################################################
619 # Patches for old functions: these functions should be considered obsolete
620 ##############################################################################
622 def _makeLoader(prefix
, sortUsing
, suiteClass
=None):
623 loader
= TestLoader()
624 loader
.sortTestMethodsUsing
= sortUsing
625 loader
.testMethodPrefix
= prefix
626 if suiteClass
: loader
.suiteClass
= suiteClass
629 def getTestCaseNames(testCaseClass
, prefix
, sortUsing
=cmp):
630 return _makeLoader(prefix
, sortUsing
).getTestCaseNames(testCaseClass
)
632 def makeSuite(testCaseClass
, prefix
='test', sortUsing
=cmp, suiteClass
=TestSuite
):
633 return _makeLoader(prefix
, sortUsing
, suiteClass
).loadTestsFromTestCase(testCaseClass
)
635 def findTestCases(module
, prefix
='test', sortUsing
=cmp, suiteClass
=TestSuite
):
636 return _makeLoader(prefix
, sortUsing
, suiteClass
).loadTestsFromModule(module
)
639 ##############################################################################
641 ##############################################################################
643 class _WritelnDecorator
:
644 """Used to decorate file-like objects with a handy 'writeln' method"""
645 def __init__(self
,stream
):
648 def __getattr__(self
, attr
):
649 return getattr(self
.stream
,attr
)
651 def writeln(self
, arg
=None):
652 if arg
: self
.write(arg
)
653 self
.write('\n') # text-mode streams translate to \r\n if needed
656 class _TextTestResult(TestResult
):
657 """A test result class that can print formatted text results to a stream.
659 Used by TextTestRunner.
661 separator1
= '=' * 70
662 separator2
= '-' * 70
664 def __init__(self
, stream
, descriptions
, verbosity
):
665 TestResult
.__init
__(self
)
667 self
.showAll
= verbosity
> 1
668 self
.dots
= verbosity
== 1
669 self
.descriptions
= descriptions
671 def getDescription(self
, test
):
672 if self
.descriptions
:
673 return test
.shortDescription() or str(test
)
677 def startTest(self
, test
):
678 TestResult
.startTest(self
, test
)
680 self
.stream
.write(self
.getDescription(test
))
681 self
.stream
.write(" ... ")
683 def addSuccess(self
, test
):
684 TestResult
.addSuccess(self
, test
)
686 self
.stream
.writeln("ok")
688 self
.stream
.write('.')
690 def addError(self
, test
, err
):
691 TestResult
.addError(self
, test
, err
)
693 self
.stream
.writeln("ERROR")
695 self
.stream
.write('E')
697 def addFailure(self
, test
, err
):
698 TestResult
.addFailure(self
, test
, err
)
700 self
.stream
.writeln("FAIL")
702 self
.stream
.write('F')
704 def printErrors(self
):
705 if self
.dots
or self
.showAll
:
706 self
.stream
.writeln()
707 self
.printErrorList('ERROR', self
.errors
)
708 self
.printErrorList('FAIL', self
.failures
)
710 def printErrorList(self
, flavour
, errors
):
711 for test
, err
in errors
:
712 self
.stream
.writeln(self
.separator1
)
713 self
.stream
.writeln("%s: %s" % (flavour
,self
.getDescription(test
)))
714 self
.stream
.writeln(self
.separator2
)
715 self
.stream
.writeln("%s" % err
)
718 class TextTestRunner
:
719 """A test runner class that displays results in textual form.
721 It prints out the names of tests as they are run, errors as they
722 occur, and a summary of the results at the end of the test run.
724 def __init__(self
, stream
=sys
.stderr
, descriptions
=1, verbosity
=1):
725 self
.stream
= _WritelnDecorator(stream
)
726 self
.descriptions
= descriptions
727 self
.verbosity
= verbosity
729 def _makeResult(self
):
730 return _TextTestResult(self
.stream
, self
.descriptions
, self
.verbosity
)
733 "Run the given test case or test suite."
734 result
= self
._makeResult
()
735 startTime
= time
.time()
737 stopTime
= time
.time()
738 timeTaken
= stopTime
- startTime
740 self
.stream
.writeln(result
.separator2
)
741 run
= result
.testsRun
742 self
.stream
.writeln("Ran %d test%s in %.3fs" %
743 (run
, run
!= 1 and "s" or "", timeTaken
))
744 self
.stream
.writeln()
745 if not result
.wasSuccessful():
746 self
.stream
.write("FAILED (")
747 failed
, errored
= len(result
.failures
), len(result
.errors
)
749 self
.stream
.write("failures=%d" % failed
)
751 if failed
: self
.stream
.write(", ")
752 self
.stream
.write("errors=%d" % errored
)
753 self
.stream
.writeln(")")
755 self
.stream
.writeln("OK")
760 ##############################################################################
761 # Facilities for running tests from the command line
762 ##############################################################################
765 """A command-line program that runs a set of tests; this is primarily
766 for making test modules conveniently executable.
769 Usage: %(progName)s [options] [test] [...]
772 -h, --help Show this message
773 -v, --verbose Verbose output
774 -q, --quiet Minimal output
777 %(progName)s - run default set of tests
778 %(progName)s MyTestSuite - run suite 'MyTestSuite'
779 %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething
780 %(progName)s MyTestCase - run all 'test*' test methods
783 def __init__(self
, module
='__main__', defaultTest
=None,
784 argv
=None, testRunner
=TextTestRunner
,
785 testLoader
=defaultTestLoader
):
786 if type(module
) == type(''):
787 self
.module
= __import__(module
)
788 for part
in module
.split('.')[1:]:
789 self
.module
= getattr(self
.module
, part
)
795 self
.defaultTest
= defaultTest
796 self
.testRunner
= testRunner
797 self
.testLoader
= testLoader
798 self
.progName
= os
.path
.basename(argv
[0])
802 def usageExit(self
, msg
=None):
804 print(self
.USAGE
% self
.__dict
__)
807 def parseArgs(self
, argv
):
810 options
, args
= getopt
.getopt(argv
[1:], 'hHvq',
811 ['help','verbose','quiet'])
812 for opt
, value
in options
:
813 if opt
in ('-h','-H','--help'):
815 if opt
in ('-q','--quiet'):
817 if opt
in ('-v','--verbose'):
819 if len(args
) == 0 and self
.defaultTest
is None:
820 self
.test
= self
.testLoader
.loadTestsFromModule(self
.module
)
823 self
.testNames
= args
825 self
.testNames
= (self
.defaultTest
,)
827 except getopt
.error
as msg
:
830 def createTests(self
):
831 self
.test
= self
.testLoader
.loadTestsFromNames(self
.testNames
,
835 if isinstance(self
.testRunner
, type):
837 testRunner
= self
.testRunner(verbosity
=self
.verbosity
)
839 # didn't accept the verbosity argument
840 testRunner
= self
.testRunner()
842 # it is assumed to be a TestRunner instance
843 testRunner
= self
.testRunner
844 result
= testRunner
.run(self
.test
)
845 sys
.exit(not result
.wasSuccessful())
850 ##############################################################################
851 # Executing this module from the command line
852 ##############################################################################
854 if __name__
== "__main__":