testtools-dev team mailing list archive
-
testtools-dev team
-
Mailing list archive
-
Message #00341
[Merge] lp:~lifeless/testtools/listtests into lp:testtools
Robert Collins has proposed merging lp:~lifeless/testtools/listtests into lp:testtools.
Requested reviews:
testtools developers (testtools-dev)
Support test listing. Not tested with discover, but I expect any damage is going to be minimal.
--
https://code.launchpad.net/~lifeless/testtools/listtests/+merge/42166
Your team testtools developers is requested to review the proposed merge of lp:~lifeless/testtools/listtests into lp:testtools.
=== modified file 'NEWS'
--- NEWS 2010-11-29 00:32:53 +0000
+++ NEWS 2010-11-29 20:36:11 +0000
@@ -42,6 +42,11 @@
* ``MatchesException`` added to the ``testtools.matchers`` module - matches
an exception class and parameters. (Robert Collins)
+* New ``KeysEqual`` matcher. (Jonathan Lange)
+
+* New helpers for conditionally importing modules, ``try_import`` and
+ ``try_imports``. (Jonathan Lange)
+
* ``Raises`` added to the ``testtools.matchers`` module - matches if the
supplied callable raises, and delegates to an optional matcher for validation
of the exception. (Robert Collins)
@@ -53,16 +58,16 @@
* ``testools.TestCase.useFixture`` has been added to glue with fixtures nicely.
(Robert Collins)
+* ``testtools.run`` now supports ``-l`` to list tests rather than executing
+ them. This is useful for integration with external test analysis/processing
+ tools like subunit and testrepository. (Robert Collins)
+
* Update documentation to say how to use testtools.run() on Python 2.4.
(Jonathan Lange, #501174)
* ``text_content`` conveniently converts a Python string to a Content object.
(Jonathan Lange, James Westby)
-* New ``KeysEqual`` matcher. (Jonathan Lange)
-
-* New helpers for conditionally importing modules, ``try_import`` and
- ``try_imports``. (Jonathan Lange)
0.9.7
=== modified file 'testtools/run.py'
--- testtools/run.py 2010-08-15 23:18:59 +0000
+++ testtools/run.py 2010-11-29 20:36:11 +0000
@@ -14,6 +14,7 @@
from testtools import TextTestResult
from testtools.compat import classtypes, istext, unicode_output_stream
+from testtools.testsuite import iterate_tests
defaultTestLoader = unittest.defaultTestLoader
@@ -34,9 +35,12 @@
class TestToolsTestRunner(object):
""" A thunk object to support unittest.TestProgram."""
+ def __init__(self, stdout):
+ self.stdout = stdout
+
def run(self, test):
"Run the given test case or test suite."
- result = TextTestResult(unicode_output_stream(sys.stdout))
+ result = TextTestResult(unicode_output_stream(self.stdout))
result.startTestRun()
try:
return test.run(result)
@@ -70,6 +74,7 @@
-h, --help Show this message
-v, --verbose Verbose output
-q, --quiet Minimal output
+ -l, --list List tests rather than executing them.
%(failfast)s%(catchbreak)s%(buffer)s
Examples:
%(progName)s test_module - run tests from test_module
@@ -87,6 +92,7 @@
-p pattern Pattern to match test files ('test*.py' default)
-t directory Top level directory of project (default to
start directory)
+ -l, --list List tests rather than executing them.
For test discovery all test modules must be importable from the top
level directory of the project.
@@ -102,11 +108,13 @@
# defaults for testing
failfast = catchbreak = buffer = progName = None
- def __init__(self, module='__main__', defaultTest=None, argv=None,
+ def __init__(self, module=__name__, defaultTest=None, argv=None,
testRunner=None, testLoader=defaultTestLoader,
exit=True, verbosity=1, failfast=None, catchbreak=None,
- buffer=None):
- if istext(module):
+ buffer=None, stdout=None):
+ if module == __name__:
+ self.module = None
+ elif istext(module):
self.module = __import__(module)
for part in module.split('.')[1:]:
self.module = getattr(self.module, part)
@@ -121,6 +129,7 @@
self.verbosity = verbosity
self.buffer = buffer
self.defaultTest = defaultTest
+ self.listtests = False
self.testRunner = testRunner
self.testLoader = testLoader
progName = argv[0]
@@ -131,7 +140,11 @@
progName = os.path.basename(argv[0])
self.progName = progName
self.parseArgs(argv)
- self.runTests()
+ if not self.listtests:
+ self.runTests()
+ else:
+ for test in iterate_tests(self.test):
+ stdout.write('%s\n' % test.id())
def usageExit(self, msg=None):
if msg:
@@ -153,9 +166,10 @@
return
import getopt
- long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer']
+ long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer',
+ 'list']
try:
- options, args = getopt.getopt(argv[1:], 'hHvqfcb', long_opts)
+ options, args = getopt.getopt(argv[1:], 'hHvqfcbl', long_opts)
for opt, value in options:
if opt in ('-h','-H','--help'):
self.usageExit()
@@ -175,14 +189,13 @@
if self.buffer is None:
self.buffer = True
# Should this raise an exception if -b is not valid?
+ if opt in ('-l', '--list'):
+ self.listtests = True
if len(args) == 0 and self.defaultTest is None:
# createTests will load tests from self.module
self.testNames = None
elif len(args) > 0:
self.testNames = args
- if __name__ == '__main__':
- # to support python -m unittest ...
- self.module = None
else:
self.testNames = (self.defaultTest,)
self.createTests()
@@ -225,6 +238,8 @@
help="Pattern to match tests ('test*.py' default)")
parser.add_option('-t', '--top-level-directory', dest='top', default=None,
help='Top level directory of project (defaults to start directory)')
+ parser.add_option('-l', '--list', dest='listtests', default=False,
+ help='List tests rather than running them.')
options, args = parser.parse_args(argv)
if len(args) > 3:
@@ -241,6 +256,7 @@
self.catchbreak = options.catchbreak
if self.buffer is None:
self.buffer = options.buffer
+ self.listtests = options.listtests
if options.verbose:
self.verbosity = 2
@@ -274,7 +290,9 @@
sys.exit(not self.result.wasSuccessful())
################
+def main(argv, stdout):
+ runner = TestToolsTestRunner(stdout)
+ program = TestProgram(argv=argv, testRunner=runner, stdout=stdout)
if __name__ == '__main__':
- runner = TestToolsTestRunner()
- program = TestProgram(argv=sys.argv, testRunner=runner)
+ main(sys.argv, sys.stdout)
=== modified file 'testtools/tests/__init__.py'
--- testtools/tests/__init__.py 2010-11-27 11:25:12 +0000
+++ testtools/tests/__init__.py 2010-11-29 20:36:11 +0000
@@ -15,13 +15,13 @@
test_helpers,
test_matchers,
test_monkey,
+ test_run,
test_runtest,
test_spinner,
test_testtools,
test_testresult,
test_testsuite,
)
- suites = []
modules = [
test_compat,
test_content,
@@ -31,12 +31,11 @@
test_helpers,
test_matchers,
test_monkey,
- test_runtest,
+ test_run,
test_spinner,
test_testresult,
test_testsuite,
test_testtools,
]
- for module in modules:
- suites.append(getattr(module, 'test_suite')())
+ suites = map(lambda x:x.test_suite(), modules)
return unittest.TestSuite(suites)
=== added file 'testtools/tests/test_run.py'
--- testtools/tests/test_run.py 1970-01-01 00:00:00 +0000
+++ testtools/tests/test_run.py 2010-11-29 20:36:11 +0000
@@ -0,0 +1,39 @@
+# Copyright (c) 2010 Testtools authors. See LICENSE for details.
+
+"""Tests for the test runner logic."""
+
+import StringIO
+
+from testtools.helpers import try_import
+fixtures = try_import('fixtures')
+
+import testtools
+from testtools import TestCase, run
+
+
+class TestRun(TestCase):
+
+ def test_run_list(self):
+ if fixtures is None:
+ self.skipTest("Need fixtures")
+ package = self.useFixture(fixtures.PythonPackage(
+ 'runexample', [('__init__.py', """
+from testtools import TestCase
+
+class TestFoo(TestCase):
+ def test_bar(self):
+ pass
+ def test_quux(self):
+ pass
+def test_suite():
+ from unittest import TestLoader
+ return TestLoader().loadTestsFromName(__name__)
+""")]))
+ testtools.__path__.append(package.base)
+ self.addCleanup(testtools.__path__.remove, package.base)
+ out = StringIO.StringIO()
+ run.main(['-l', 'testtools.runexample.test_suite'], out)
+
+def test_suite():
+ from unittest import TestLoader
+ return TestLoader().loadTestsFromName(__name__)
Follow ups