Fixed #11613: Added a failfast option for test running. Thanks jukvalim and Randy Barlow.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@11843 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Karen Tracey 2009-12-13 16:24:36 +00:00
parent d10dd3eceb
commit 92eec3ef9a
4 changed files with 45 additions and 7 deletions

View File

@ -6,6 +6,8 @@ class Command(BaseCommand):
option_list = BaseCommand.option_list + ( option_list = BaseCommand.option_list + (
make_option('--noinput', action='store_false', dest='interactive', default=True, make_option('--noinput', action='store_false', dest='interactive', default=True,
help='Tells Django to NOT prompt the user for input of any kind.'), help='Tells Django to NOT prompt the user for input of any kind.'),
make_option('--failfast', action='store_true', dest='failfast', default=False,
help='Tells Django to stop running the test suite after first failed test.')
) )
help = 'Runs the test suite for the specified applications, or the entire site if no apps are specified.' help = 'Runs the test suite for the specified applications, or the entire site if no apps are specified.'
args = '[appname ...]' args = '[appname ...]'
@ -15,11 +17,18 @@ class Command(BaseCommand):
def handle(self, *test_labels, **options): def handle(self, *test_labels, **options):
from django.conf import settings from django.conf import settings
from django.test.utils import get_runner from django.test.utils import get_runner
verbosity = int(options.get('verbosity', 1)) verbosity = int(options.get('verbosity', 1))
interactive = options.get('interactive', True) interactive = options.get('interactive', True)
failfast = options.get('failfast', False)
test_runner = get_runner(settings) test_runner = get_runner(settings)
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive) # Some custom test runners won't accept the failfast flag, so let's make sure they accept it before passing it to them
if 'failfast' in test_runner.func_code.co_varnames:
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive,
failfast=failfast)
else:
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
if failures: if failures:
sys.exit(failures) sys.exit(failures)

View File

@ -10,6 +10,26 @@ TEST_MODULE = 'tests'
doctestOutputChecker = OutputChecker() doctestOutputChecker = OutputChecker()
class DjangoTestRunner(unittest.TextTestRunner):
def __init__(self, verbosity=0, failfast=False, **kwargs):
super(DjangoTestRunner, self).__init__(verbosity=verbosity, **kwargs)
self.failfast = failfast
def _makeResult(self):
result = super(DjangoTestRunner, self)._makeResult()
failfast = self.failfast
def stoptest_override(func):
def stoptest(test):
if failfast and not result.wasSuccessful():
result.stop()
func(test)
return stoptest
setattr(result, 'stopTest', stoptest_override(result.stopTest))
return result
def get_tests(app_module): def get_tests(app_module):
try: try:
app_path = app_module.__name__.split('.')[:-1] app_path = app_module.__name__.split('.')[:-1]
@ -146,7 +166,7 @@ def reorder_suite(suite, classes):
bins[0].addTests(bins[i+1]) bins[0].addTests(bins[i+1])
return bins[0] return bins[0]
def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]): def run_tests(test_labels, verbosity=1, interactive=True, failfast=False, extra_tests=[]):
""" """
Run the unit tests for all the test labels in the provided list. Run the unit tests for all the test labels in the provided list.
Labels must be of the form: Labels must be of the form:
@ -189,7 +209,7 @@ def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]):
old_name = settings.DATABASE_NAME old_name = settings.DATABASE_NAME
from django.db import connection from django.db import connection
connection.creation.create_test_db(verbosity, autoclobber=not interactive) connection.creation.create_test_db(verbosity, autoclobber=not interactive)
result = unittest.TextTestRunner(verbosity=verbosity).run(suite) result = DjangoTestRunner(verbosity=verbosity, failfast=failfast).run(suite)
connection.creation.destroy_test_db(old_name, verbosity) connection.creation.destroy_test_db(old_name, verbosity)
teardown_test_environment() teardown_test_environment()

View File

@ -696,6 +696,12 @@ test <app or test identifier>
Runs tests for all installed models. See :ref:`topics-testing` for more Runs tests for all installed models. See :ref:`topics-testing` for more
information. information.
--failfast
~~~~~~~~
Use the ``--failfast`` option to stop running tests and report the failure
immediately after a test fails.
testserver <fixture fixture ...> testserver <fixture fixture ...>
-------------------------------- --------------------------------

View File

@ -86,7 +86,7 @@ class InvalidModelTestCase(unittest.TestCase):
self.assert_(not unexpected, "Unexpected Errors: " + '\n'.join(unexpected)) self.assert_(not unexpected, "Unexpected Errors: " + '\n'.join(unexpected))
self.assert_(not missing, "Missing Errors: " + '\n'.join(missing)) self.assert_(not missing, "Missing Errors: " + '\n'.join(missing))
def django_tests(verbosity, interactive, test_labels): def django_tests(verbosity, interactive, failfast, test_labels):
from django.conf import settings from django.conf import settings
old_installed_apps = settings.INSTALLED_APPS old_installed_apps = settings.INSTALLED_APPS
@ -160,7 +160,8 @@ def django_tests(verbosity, interactive, test_labels):
settings.TEST_RUNNER = 'django.test.simple.run_tests' settings.TEST_RUNNER = 'django.test.simple.run_tests'
test_runner = get_runner(settings) test_runner = get_runner(settings)
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive, extra_tests=extra_tests) failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive, failfast=failfast,
extra_tests=extra_tests)
if failures: if failures:
sys.exit(failures) sys.exit(failures)
@ -182,6 +183,8 @@ if __name__ == "__main__":
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output') help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
parser.add_option('--noinput', action='store_false', dest='interactive', default=True, parser.add_option('--noinput', action='store_false', dest='interactive', default=True,
help='Tells Django to NOT prompt the user for input of any kind.') help='Tells Django to NOT prompt the user for input of any kind.')
parser.add_option('--failfast', action='store_true', dest='failfast', default=False,
help='Tells Django to stop running the test suite after first failed test.')
parser.add_option('--settings', parser.add_option('--settings',
help='Python path to settings module, e.g. "myproject.settings". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.') help='Python path to settings module, e.g. "myproject.settings". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.')
options, args = parser.parse_args() options, args = parser.parse_args()
@ -190,4 +193,4 @@ if __name__ == "__main__":
elif "DJANGO_SETTINGS_MODULE" not in os.environ: elif "DJANGO_SETTINGS_MODULE" not in os.environ:
parser.error("DJANGO_SETTINGS_MODULE is not set in the environment. " parser.error("DJANGO_SETTINGS_MODULE is not set in the environment. "
"Set it or use --settings.") "Set it or use --settings.")
django_tests(int(options.verbosity), options.interactive, args) django_tests(int(options.verbosity), options.interactive, options.failfast, args)