Browse Source

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.
Tim Graham 11 years ago
parent
commit
4ba373840a

+ 8 - 4
django/test/runner.py

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

+ 8 - 0
docs/releases/1.7.txt

@@ -285,6 +285,14 @@ Templates
 * ``TypeError`` exceptions are not longer silenced when raised during the
   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
 =====================================
 

+ 19 - 0
docs/topics/testing/advanced.txt

@@ -338,6 +338,25 @@ execute and tear down the test suite.
 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
 
     This is the class that loads tests, whether from TestCases or modules or

+ 10 - 1
tests/test_runner/test_discover_runner.py

@@ -1,7 +1,7 @@
 from contextlib import contextmanager
 import os
 import sys
-from unittest import expectedFailure
+from unittest import expectedFailure, TestSuite, TextTestRunner, defaultTestLoader
 
 from django.test import TestCase
 from django.test.runner import DiscoverRunner
@@ -68,3 +68,12 @@ class DiscoverRunnerTest(TestCase):
             ).countTestCases()
 
         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)