root/trunk/test/simple.py @ 149

Revision 149, 4.8 kB (checked in by akaihola, 3 years ago)

[test] Completed the test.simple.run_tests_until_fail() utility.

  • Property svn:eol-style set to native
Line 
1__doc__ = """
2In your settings, use
3
4  TEST_RUNNER = 'ambidjangolib.test.simple.run_tests_until_fail'
5
6to make `manage.py test` stop after the first test suite with failures, and
7only show the first failing test in the suite.
8"""
9
10
11from unittest import \
12     TestSuite, TextTestRunner, _TextTestResult, defaultTestLoader
13from django.test import _doctest as doctest
14from django.conf import settings
15from django.db import transaction
16from django.db.models import get_app, get_apps
17from django.test.utils import \
18     setup_test_environment, teardown_test_environment, \
19     create_test_db, destroy_test_db
20from django.test.simple import build_test, get_tests, doctestOutputChecker
21from django.test.testcases import DocTestRunner
22
23
24class DocTestRunner(doctest.DocTestRunner):
25    """
26    Replacement for django.test.testcases.DocTestRunner which unfortunately
27    overrides any supplied `optionflags=` kwarg with only `doctest.ELLIPSIS`.
28    We need to pass on optionflags.
29    """
30    def __init__(self, *args, **kwargs):
31        doctest.DocTestRunner.__init__(self, *args, **kwargs)
32        self.optionflags |= doctest.ELLIPSIS
33        # Django's original has `=` instead of `|=` here
34
35    def report_unexpected_exception(self, out, test, example, exc_info):
36        doctest.DocTestRunner.report_unexpected_exception(self, out, test,
37                                                          example, exc_info)
38        # Rollback, in case of database errors. Otherwise they'd have
39        # side effects on other tests.
40        transaction.rollback_unless_managed()
41
42
43def _add_tests_for_module(suite, module):
44    """
45    Repeated code from inside `build_suite()` is refactored here.
46    """
47    # Load unit and doctests in the given module. If module has a suite()
48    # method, use it. Otherwise build the test suite ourselves.
49    if hasattr(module, 'suite'):
50        suite.addTest(module.suite())
51    else:
52        suite.addTest(defaultTestLoader.loadTestsFromModule(module))
53        try:
54            suite.addTest(doctest.DocTestSuite(
55                module,
56                checker=doctestOutputChecker,
57                runner=DocTestRunner,
58                optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
59        except ValueError:
60            # No doc tests in models.py
61            pass
62
63
64def build_suite(app_module):
65    """
66    Create a complete Django test suite for the provided application module.
67
68    This overrides Django's original `django.test.simple.build_suite()` because
69    we need to pass the `REPORT_ONLY_FIRST_FAILURE` option flag to
70    `DocTestSuite` instances.
71    """
72    suite = TestSuite()
73
74    _add_tests_for_module(suite, app_module)
75
76    # Check to see if a separate 'tests' module exists parallel to the
77    # models module
78    test_module = get_tests(app_module)
79    if test_module:
80        _add_tests_for_module(suite, test_module)
81
82    return suite
83
84
85class _FailStopTextTestResult(_TextTestResult):
86    def addError(self, test, err):
87        _TextTestResult.addError(self, test, err)
88        self.shouldStop = True
89
90    def addFailure(self, test, err):
91        _TextTestResult.addFailure(self, test, err)
92        self.shouldStop = True
93
94
95class FailStopTextTestRunner(TextTestRunner):
96    def _makeResult(self):
97        return _FailStopTextTestResult(
98            self.stream, self.descriptions, self.verbosity)
99
100
101def run_tests_until_fail(test_labels, verbosity=1, interactive=True, extra_tests=[]):
102    """
103    Run the unit tests for all the test labels in the provided list.
104    Labels must be of the form:
105     - app.TestClass.test_method
106        Run a single specific test method
107     - app.TestClass
108        Run all the test methods in a given class
109     - app
110        Search for doctests and unittests in the named application.
111
112    When looking for tests, the test runner will look in the models and
113    tests modules for the application.
114
115    A list of 'extra' tests may also be provided; these tests
116    will be added to the test suite.
117
118    Stops the tests at the first failure and returns 1.  If all test pass,
119    returns 0.
120
121    Also displays only the first failure in the failing test suite.
122    """
123    setup_test_environment()
124
125    settings.DEBUG = False
126    suite = TestSuite()
127
128    if test_labels:
129        for label in test_labels:
130            if '.' in label:
131                suite.addTest(build_test(label))
132            else:
133                app = get_app(label)
134                suite.addTest(build_suite(app))
135    else:
136        for app in get_apps():
137            suite.addTest(build_suite(app))
138
139    for test in extra_tests:
140        suite.addTest(test)
141
142    old_name = settings.DATABASE_NAME
143    create_test_db(verbosity, autoclobber=not interactive)
144    result = FailStopTextTestRunner(verbosity=verbosity).run(suite)
145    destroy_test_db(old_name, verbosity)
146
147    teardown_test_environment()
148
149    return len(result.failures) + len(result.errors)
Note: See TracBrowser for help on using the browser.