Fixed #16534 -- Improved ability to customize DiscoverRunner

Added DiscoverRunner.test_suite and .test_runner attributes.

Thanks tomchristie for the suggestion and jcd for the patch.
This commit is contained in:
Tim Graham 2013-09-10 09:49:39 -04:00
parent fca4c4826e
commit 4ba373840a
4 changed files with 45 additions and 5 deletions

View File

@ -14,6 +14,8 @@ class DiscoverRunner(object):
A Django test runner that uses unittest2 test discovery. A Django test runner that uses unittest2 test discovery.
""" """
test_suite = TestSuite
test_runner = unittest.TextTestRunner
test_loader = defaultTestLoader test_loader = defaultTestLoader
reorder_by = (TestCase, ) reorder_by = (TestCase, )
option_list = ( option_list = (
@ -42,7 +44,7 @@ class DiscoverRunner(object):
unittest.installHandler() unittest.installHandler()
def build_suite(self, test_labels=None, extra_tests=None, **kwargs): def build_suite(self, test_labels=None, extra_tests=None, **kwargs):
suite = TestSuite() suite = self.test_suite()
test_labels = test_labels or ['.'] test_labels = test_labels or ['.']
extra_tests = extra_tests or [] extra_tests = extra_tests or []
@ -107,7 +109,7 @@ class DiscoverRunner(object):
return setup_databases(self.verbosity, self.interactive, **kwargs) return setup_databases(self.verbosity, self.interactive, **kwargs)
def run_suite(self, suite, **kwargs): def run_suite(self, suite, **kwargs):
return unittest.TextTestRunner( return self.test_runner(
verbosity=self.verbosity, verbosity=self.verbosity,
failfast=self.failfast, failfast=self.failfast,
).run(suite) ).run(suite)
@ -201,7 +203,8 @@ def reorder_suite(suite, classes):
classes[1], etc. Tests with no match in classes are placed last. classes[1], etc. Tests with no match in classes are placed last.
""" """
class_count = len(classes) class_count = len(classes)
bins = [unittest.TestSuite() for i in range(class_count+1)] suite_class = type(suite)
bins = [suite_class() for i in range(class_count+1)]
partition_suite(suite, classes, bins) partition_suite(suite, classes, bins)
for i in range(class_count): for i in range(class_count):
bins[0].addTests(bins[i+1]) bins[0].addTests(bins[i+1])
@ -218,8 +221,9 @@ def partition_suite(suite, classes, bins):
Tests of type classes[i] are added to bins[i], Tests of type classes[i] are added to bins[i],
tests with no match found in classes are place in bins[-1] tests with no match found in classes are place in bins[-1]
""" """
suite_class = type(suite)
for test in suite: for test in suite:
if isinstance(test, unittest.TestSuite): if isinstance(test, suite_class):
partition_suite(test, classes, bins) partition_suite(test, classes, bins)
else: else:
for i in range(len(classes)): for i in range(len(classes)):

View File

@ -285,6 +285,14 @@ Templates
* ``TypeError`` exceptions are not longer silenced when raised during the * ``TypeError`` exceptions are not longer silenced when raised during the
rendering of a template. rendering of a template.
Tests
^^^^^
* :class:`~django.test.runner.DiscoverRunner` has two new attributes,
:attr:`~django.test.runner.DiscoverRunner.test_suite` and
:attr:`~django.test.runner.DiscoverRunner.test_runner`, which facilitate
overriding the way tests are collected and run.
Backwards incompatible changes in 1.7 Backwards incompatible changes in 1.7
===================================== =====================================

View File

@ -338,6 +338,25 @@ execute and tear down the test suite.
Attributes Attributes
~~~~~~~~~~ ~~~~~~~~~~
.. attribute:: DiscoverRunner.test_suite
.. versionadded:: 1.7
The class used to build the test suite. By default it is set to
``unittest.TestSuite``. This can be overridden if you wish to implement
different logic for collecting tests.
.. attribute:: DiscoverRunner.test_runner
.. versionadded:: 1.7
This is the class of the low-level test runner which is used to execute
the individual tests and format the results. By default it is set to
``unittest.TextTestRunner``. Despite the unfortunate similarity in
naming conventions, this is not the same type of class as
``DiscoverRunner``, which covers a broader set of responsibilites. You
can override this attribute to modify the way tests are run and reported.
.. attribute:: DiscoverRunner.test_loader .. attribute:: DiscoverRunner.test_loader
This is the class that loads tests, whether from TestCases or modules or This is the class that loads tests, whether from TestCases or modules or

View File

@ -1,7 +1,7 @@
from contextlib import contextmanager from contextlib import contextmanager
import os import os
import sys import sys
from unittest import expectedFailure from unittest import expectedFailure, TestSuite, TextTestRunner, defaultTestLoader
from django.test import TestCase from django.test import TestCase
from django.test.runner import DiscoverRunner from django.test.runner import DiscoverRunner
@ -68,3 +68,12 @@ class DiscoverRunnerTest(TestCase):
).countTestCases() ).countTestCases()
self.assertEqual(count, 3) self.assertEqual(count, 3)
def test_overrideable_test_suite(self):
self.assertEqual(DiscoverRunner().test_suite, TestSuite)
def test_overrideable_test_runner(self):
self.assertEqual(DiscoverRunner().test_runner, TextTestRunner)
def test_overrideable_test_loader(self):
self.assertEqual(DiscoverRunner().test_loader, defaultTestLoader)