Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #11613: Added a failfast option for test running. Thanks jukval…

…im and Randy Barlow.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@11843 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 92eec3ef9ae3eed18cef4cb7e87a85df4f06d3f0 1 parent d10dd3e
Karen Tracey authored December 13, 2009
13  django/core/management/commands/test.py
@@ -6,6 +6,8 @@ class Command(BaseCommand):
6 6
     option_list = BaseCommand.option_list + (
7 7
         make_option('--noinput', action='store_false', dest='interactive', default=True,
8 8
             help='Tells Django to NOT prompt the user for input of any kind.'),
  9
+        make_option('--failfast', action='store_true', dest='failfast', default=False,
  10
+            help='Tells Django to stop running the test suite after first failed test.')
9 11
     )
10 12
     help = 'Runs the test suite for the specified applications, or the entire site if no apps are specified.'
11 13
     args = '[appname ...]'
@@ -15,11 +17,18 @@ class Command(BaseCommand):
15 17
     def handle(self, *test_labels, **options):
16 18
         from django.conf import settings
17 19
         from django.test.utils import get_runner
18  
-
  20
+        
19 21
         verbosity = int(options.get('verbosity', 1))
20 22
         interactive = options.get('interactive', True)
  23
+        failfast = options.get('failfast', False)
21 24
         test_runner = get_runner(settings)
22 25
 
23  
-        failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
  26
+        # Some custom test runners won't accept the failfast flag, so let's make sure they accept it before passing it to them
  27
+        if 'failfast' in test_runner.func_code.co_varnames:
  28
+            failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive, 
  29
+                                   failfast=failfast)
  30
+        else:
  31
+            failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
  32
+
24 33
         if failures:
25 34
             sys.exit(failures)
24  django/test/simple.py
@@ -10,6 +10,26 @@
10 10
 
11 11
 doctestOutputChecker = OutputChecker()
12 12
 
  13
+class DjangoTestRunner(unittest.TextTestRunner):
  14
+    
  15
+    def __init__(self, verbosity=0, failfast=False, **kwargs):
  16
+        super(DjangoTestRunner, self).__init__(verbosity=verbosity, **kwargs)
  17
+        self.failfast = failfast
  18
+        
  19
+    def _makeResult(self):
  20
+        result = super(DjangoTestRunner, self)._makeResult()
  21
+        failfast = self.failfast
  22
+        
  23
+        def stoptest_override(func):
  24
+            def stoptest(test):
  25
+                if failfast and not result.wasSuccessful():
  26
+                    result.stop()
  27
+                func(test)
  28
+            return stoptest
  29
+        
  30
+        setattr(result, 'stopTest', stoptest_override(result.stopTest))
  31
+        return result
  32
+
13 33
 def get_tests(app_module):
14 34
     try:
15 35
         app_path = app_module.__name__.split('.')[:-1]
@@ -146,7 +166,7 @@ def reorder_suite(suite, classes):
146 166
         bins[0].addTests(bins[i+1])
147 167
     return bins[0]
148 168
 
149  
-def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]):
  169
+def run_tests(test_labels, verbosity=1, interactive=True, failfast=False, extra_tests=[]):
150 170
     """
151 171
     Run the unit tests for all the test labels in the provided list.
152 172
     Labels must be of the form:
@@ -189,7 +209,7 @@ def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]):
189 209
     old_name = settings.DATABASE_NAME
190 210
     from django.db import connection
191 211
     connection.creation.create_test_db(verbosity, autoclobber=not interactive)
192  
-    result = unittest.TextTestRunner(verbosity=verbosity).run(suite)
  212
+    result = DjangoTestRunner(verbosity=verbosity, failfast=failfast).run(suite)
193 213
     connection.creation.destroy_test_db(old_name, verbosity)
194 214
 
195 215
     teardown_test_environment()
6  docs/ref/django-admin.txt
@@ -696,6 +696,12 @@ test <app or test identifier>
696 696
 Runs tests for all installed models. See :ref:`topics-testing` for more
697 697
 information.
698 698
 
  699
+--failfast
  700
+~~~~~~~~
  701
+
  702
+Use the ``--failfast`` option to stop running tests and report the failure 
  703
+immediately after a test fails.
  704
+
699 705
 testserver <fixture fixture ...>
700 706
 --------------------------------
701 707
 
9  tests/runtests.py
@@ -86,7 +86,7 @@ def runTest(self):
86 86
         self.assert_(not unexpected, "Unexpected Errors: " + '\n'.join(unexpected))
87 87
         self.assert_(not missing, "Missing Errors: " + '\n'.join(missing))
88 88
 
89  
-def django_tests(verbosity, interactive, test_labels):
  89
+def django_tests(verbosity, interactive, failfast, test_labels):
90 90
     from django.conf import settings
91 91
 
92 92
     old_installed_apps = settings.INSTALLED_APPS
@@ -160,7 +160,8 @@ def django_tests(verbosity, interactive, test_labels):
160 160
         settings.TEST_RUNNER = 'django.test.simple.run_tests'
161 161
     test_runner = get_runner(settings)
162 162
 
163  
-    failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive, extra_tests=extra_tests)
  163
+    failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive, failfast=failfast,
  164
+                           extra_tests=extra_tests)
164 165
     if failures:
165 166
         sys.exit(failures)
166 167
 
@@ -182,6 +183,8 @@ def django_tests(verbosity, interactive, test_labels):
182 183
         help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
183 184
     parser.add_option('--noinput', action='store_false', dest='interactive', default=True,
184 185
         help='Tells Django to NOT prompt the user for input of any kind.')
  186
+    parser.add_option('--failfast', action='store_true', dest='failfast', default=False,
  187
+        help='Tells Django to stop running the test suite after first failed test.')
185 188
     parser.add_option('--settings',
186 189
         help='Python path to settings module, e.g. "myproject.settings". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.')
187 190
     options, args = parser.parse_args()
@@ -190,4 +193,4 @@ def django_tests(verbosity, interactive, test_labels):
190 193
     elif "DJANGO_SETTINGS_MODULE" not in os.environ:
191 194
         parser.error("DJANGO_SETTINGS_MODULE is not set in the environment. "
192 195
                       "Set it or use --settings.")
193  
-    django_tests(int(options.verbosity), options.interactive, args)
  196
+    django_tests(int(options.verbosity), options.interactive, options.failfast, args)

0 notes on commit 92eec3e

Please sign in to comment.
Something went wrong with that request. Please try again.