Allow customisation of testtools.run --list-tests behaviour.

This commit is contained in:
Robert Collins
2013-04-03 18:55:01 +13:00
parent 088f82f4dd
commit 43f565ef2c
4 changed files with 63 additions and 17 deletions

4
NEWS
View File

@@ -91,6 +91,10 @@ Improvements
partitioning analysis of events or sending feedback encapsulated in partitioning analysis of events or sending feedback encapsulated in
StreamResult events back to their source. (Robert Collins) StreamResult events back to their source. (Robert Collins)
* ``testtools.run.TestProgram`` now supports the ``TestRunner`` taking over
responsibility for formatting the output of ``--list-tests``.
(Robert Collins)
* The error message for setUp and tearDown upcall errors was broken on Python * The error message for setUp and tearDown upcall errors was broken on Python
3.4. (Monty Taylor, Robert Collins, #1140688) 3.4. (Monty Taylor, Robert Collins, #1140688)

View File

@@ -431,6 +431,13 @@ If you a writing custom wrapping suites, consider implementing filter_by_ids
to support this (though most wrappers that subclass ``unittest.TestSuite`` will to support this (though most wrappers that subclass ``unittest.TestSuite`` will
work just fine [see ``testtools.testsuite.filter_by_ids`` for details.] work just fine [see ``testtools.testsuite.filter_by_ids`` for details.]
Extensions to TestRunner
========================
To facilitate custom listing of tests, ``testtools.run.TestProgram`` attempts
to call ``list`` on the ``TestRunner``, falling back to a generic
implementation if it is not present.
.. _`testtools API docs`: http://mumak.net/testtools/apidocs/ .. _`testtools API docs`: http://mumak.net/testtools/apidocs/
.. _unittest: http://docs.python.org/library/unittest.html .. _unittest: http://docs.python.org/library/unittest.html
.. _fixture: http://pypi.python.org/pypi/fixtures .. _fixture: http://pypi.python.org/pypi/fixtures

View File

@@ -8,10 +8,13 @@ For instance, to run the testtools test suite.
$ python -m testtools.run testtools.tests.test_suite $ python -m testtools.run testtools.tests.test_suite
""" """
from functools import partial
import os import os
import unittest import unittest
import sys import sys
from extras import safe_hasattr
from testtools import TextTestResult from testtools import TextTestResult
from testtools.compat import classtypes, istext, unicode_output_stream from testtools.compat import classtypes, istext, unicode_output_stream
from testtools.testsuite import filter_by_ids, iterate_tests, sorted_tests from testtools.testsuite import filter_by_ids, iterate_tests, sorted_tests
@@ -35,14 +38,22 @@ else:
class TestToolsTestRunner(object): class TestToolsTestRunner(object):
""" A thunk object to support unittest.TestProgram.""" """ A thunk object to support unittest.TestProgram."""
def __init__(self, verbosity=None, failfast=None, buffer=None): def __init__(self, verbosity=None, failfast=None, buffer=None,
stdout=None):
"""Create a TestToolsTestRunner. """Create a TestToolsTestRunner.
:param verbosity: Ignored. :param verbosity: Ignored.
:param failfast: Stop running tests at the first failure. :param failfast: Stop running tests at the first failure.
:param buffer: Ignored. :param buffer: Ignored.
:param stdout: Stream to use for stdout.
""" """
self.failfast = failfast self.failfast = failfast
self.stdout = stdout
def list(self, test):
"""List the tests that would be run if test() was run."""
for test in iterate_tests(test):
self.stdout.write('%s\n' % test.id())
def run(self, test): def run(self, test):
"Run the given test case or test suite." "Run the given test case or test suite."
@@ -177,8 +188,12 @@ class TestProgram(object):
if not self.listtests: if not self.listtests:
self.runTests() self.runTests()
else: else:
for test in iterate_tests(self.test): runner = self._get_runner()
stdout.write('%s\n' % test.id()) if safe_hasattr(runner, 'list'):
runner.list(self.test)
else:
for test in iterate_tests(self.test):
stdout.write('%s\n' % test.id())
def usageExit(self, msg=None): def usageExit(self, msg=None):
if msg: if msg:
@@ -321,26 +336,32 @@ class TestProgram(object):
if (self.catchbreak if (self.catchbreak
and getattr(unittest, 'installHandler', None) is not None): and getattr(unittest, 'installHandler', None) is not None):
unittest.installHandler() unittest.installHandler()
if self.testRunner is None: testRunner = self._get_runner()
self.testRunner = TestToolsTestRunner
if isinstance(self.testRunner, classtypes()):
try:
testRunner = self.testRunner(verbosity=self.verbosity,
failfast=self.failfast,
buffer=self.buffer)
except TypeError:
# didn't accept the verbosity, buffer or failfast arguments
testRunner = self.testRunner()
else:
# it is assumed to be a TestRunner instance
testRunner = self.testRunner
self.result = testRunner.run(self.test) self.result = testRunner.run(self.test)
if self.exit: if self.exit:
sys.exit(not self.result.wasSuccessful()) sys.exit(not self.result.wasSuccessful())
def _get_runner(self):
if self.testRunner is None:
self.testRunner = TestToolsTestRunner
try:
testRunner = self.testRunner(verbosity=self.verbosity,
failfast=self.failfast,
buffer=self.buffer)
except TypeError:
# didn't accept the verbosity, buffer or failfast arguments
try:
testRunner = self.testRunner()
except TypeError:
# it is assumed to be a TestRunner instance
testRunner = self.testRunner
return testRunner
################ ################
def main(argv, stdout): def main(argv, stdout):
program = TestProgram(argv=argv, testRunner=TestToolsTestRunner, program = TestProgram(argv=argv, testRunner=partial(TestToolsTestRunner, stdout=stdout),
stdout=stdout) stdout=stdout)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -97,6 +97,20 @@ class TestRun(TestCase):
if fixtures is None: if fixtures is None:
self.skipTest("Need fixtures") self.skipTest("Need fixtures")
def test_run_custom_list(self):
self.useFixture(SampleTestFixture())
tests = []
class CaptureList(run.TestToolsTestRunner):
def list(self, test):
tests.append(set([case.id() for case
in testtools.testsuite.iterate_tests(test)]))
out = StringIO()
program = run.TestProgram(
argv=['prog', '-l', 'testtools.runexample.test_suite'],
stdout=out, testRunner=CaptureList)
self.assertEqual([set(['testtools.runexample.TestFoo.test_bar',
'testtools.runexample.TestFoo.test_quux'])], tests)
def test_run_list(self): def test_run_list(self):
self.useFixture(SampleTestFixture()) self.useFixture(SampleTestFixture())
out = StringIO() out = StringIO()