Permalink
Browse files

Fixed #12624 -- Modified test runners to be class based.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12255 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent 34e4201 commit 53b61d9c023a77a5718ace9b9816ce647349ea99 @freakboy3742 freakboy3742 committed Jan 18, 2010
@@ -487,8 +487,8 @@
# TESTING #
###########
-# The name of the method to use to invoke the test suite
-TEST_RUNNER = 'django.test.simple.run_tests'
+# The name of the class to use to run the test suite
+TEST_RUNNER = 'django.test.simple.DjangoTestSuiteRunner'
# The name of the database to use for testing purposes.
# If None, a name of 'test_' + DATABASE_NAME will be assumed
@@ -21,14 +21,20 @@ def handle(self, *test_labels, **options):
verbosity = int(options.get('verbosity', 1))
interactive = options.get('interactive', True)
failfast = options.get('failfast', False)
- test_runner = get_runner(settings)
+ TestRunner = get_runner(settings)
- # 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)
+ if hasattr(TestRunner, 'func_name'):
+ # Pre 1.2 test runners were just functions,
+ # and did not support the 'failfast' option.
+ import warnings
+ warnings.warn(
+ 'Function-based test runners are deprecated. Test runners should be classes with a run_tests() method.',
+ PendingDeprecationWarning
+ )
+ failures = TestRunner(test_labels, verbosity=verbosity, interactive=interactive)
else:
- failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
+ test_runner = TestRunner(verbosity=verbosity, interactive=interactive, failfast=failfast)
+ failures = test_runner.run_tests(test_labels)
if failures:
sys.exit(bool(failures))
View
@@ -40,7 +40,7 @@ def _keyboard_interrupt_handler(self, signal_number, stack_frame):
"""
self._keyboard_interrupt_intercepted = True
sys.stderr.write(" <Test run halted by Ctrl-C> ")
- # Set the interrupt handler back to the default handler, so that
+ # Set the interrupt handler back to the default handler, so that
# another Ctrl-C press will trigger immediate exit.
signal.signal(signal.SIGINT, self._default_keyboard_interrupt_handler)
@@ -197,56 +197,97 @@ def reorder_suite(suite, classes):
bins[0].addTests(bins[i+1])
return bins[0]
-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.
- Labels must be of the form:
- - app.TestClass.test_method
- Run a single specific test method
- - app.TestClass
- Run all the test methods in a given class
- - app
- Search for doctests and unittests in the named application.
-
- When looking for tests, the test runner will look in the models and
- tests modules for the application.
-
- A list of 'extra' tests may also be provided; these tests
- will be added to the test suite.
-
- Returns the number of tests that failed.
- """
- setup_test_environment()
- settings.DEBUG = False
- suite = unittest.TestSuite()
+class DjangoTestSuiteRunner(object):
+ def __init__(self, verbosity=1, interactive=True, failfast=True):
+ self.verbosity = verbosity
+ self.interactive = interactive
+ self.failfast = failfast
- if test_labels:
- for label in test_labels:
- if '.' in label:
- suite.addTest(build_test(label))
- else:
- app = get_app(label)
+ def setup_test_environment(self):
+ setup_test_environment()
+ settings.DEBUG = False
+
+ def build_suite(self, test_labels, extra_tests=None):
+ suite = unittest.TestSuite()
+
+ if test_labels:
+ for label in test_labels:
+ if '.' in label:
+ suite.addTest(build_test(label))
+ else:
+ app = get_app(label)
+ suite.addTest(build_suite(app))
+ else:
+ for app in get_apps():
suite.addTest(build_suite(app))
- else:
- for app in get_apps():
- suite.addTest(build_suite(app))
- for test in extra_tests:
- suite.addTest(test)
+ if extra_tests:
+ for test in extra_tests:
+ suite.addTest(test)
+
+ return reorder_suite(suite, (TestCase,))
+
+ def setup_databases(self):
+ from django.db import connections
+ old_names = []
+ for alias in connections:
+ connection = connections[alias]
+ old_names.append((connection, connection.settings_dict['NAME']))
+ connection.creation.create_test_db(self.verbosity, autoclobber=not self.interactive)
+ return old_names
+
+ def run_suite(self, suite):
+ return DjangoTestRunner(verbosity=self.verbosity, failfast=self.failfast).run(suite)
+
+ def teardown_databases(self, old_names):
+ for connection, old_name in old_names:
+ connection.creation.destroy_test_db(old_name, self.verbosity)
+
+ def teardown_test_environment(self):
+ teardown_test_environment()
+
+ def suite_result(self, result):
+ return len(result.failures) + len(result.errors)
+
+ def run_tests(self, test_labels, extra_tests=None):
+ """
+ Run the unit tests for all the test labels in the provided list.
+ Labels must be of the form:
+ - app.TestClass.test_method
+ Run a single specific test method
+ - app.TestClass
+ Run all the test methods in a given class
+ - app
+ Search for doctests and unittests in the named application.
+
+ When looking for tests, the test runner will look in the models and
+ tests modules for the application.
+
+ A list of 'extra' tests may also be provided; these tests
+ will be added to the test suite.
+
+ Returns the number of tests that failed.
+ """
+ self.setup_test_environment()
+
+ old_names = self.setup_databases()
+
+ suite = self.build_suite(test_labels, extra_tests)
+
+ result = self.run_suite(suite)
- suite = reorder_suite(suite, (TestCase,))
+ self.teardown_databases(old_names)
- from django.db import connections
- old_names = []
- for alias in connections:
- connection = connections[alias]
- old_names.append((connection, connection.settings_dict['NAME']))
- connection.creation.create_test_db(verbosity, autoclobber=not interactive)
- result = DjangoTestRunner(verbosity=verbosity, failfast=failfast).run(suite)
- for connection, old_name in old_names:
- connection.creation.destroy_test_db(old_name, verbosity)
+ self.teardown_test_environment()
- teardown_test_environment()
+ return self.suite_result(result)
- return len(result.failures) + len(result.errors)
+def run_tests(test_labels, verbosity=1, interactive=True, failfast=False, extra_tests=None):
+ import warnings
+ warnings.warn(
+ 'The run_tests() test runner has been deprecated in favor of DjangoTestSuiteRunner.',
+ PendingDeprecationWarning
+ )
+ test_runner = DjangoTestSuiteRunner(verbosity=verbosity, interactive=interactive, failfast=failfast)
+ return test_runner.run_tests(test_labels, extra_tests=extra_tests)
@@ -74,6 +74,9 @@ their deprecation, as per the :ref:`Django deprecation policy
``django.utils.formats.get_format()`` to get the appropriate
formats.
+ * The ability to use a function-based test runners will be removed,
+ along with the ``django.test.simple.run_tests()`` test runner.
+
* 2.0
* ``django.views.defaults.shortcut()``. This function has been moved
to ``django.contrib.contenttypes.views.shortcut()`` as part of the
View
@@ -367,6 +367,14 @@ An undocumented regex for validating email addresses has been moved from
django.form.fields to django.core.validators. You will need to update
your imports if you are using it.
+Function-based test runners
+---------------------------
+
+Django 1.2 changes the test runner tools to use a class-based
+approach. Old style function-based test runners will still work, but
+should be updated to use the new :ref:`class-based runners
+<topics-testing-test_runner>`.
+
What's new in Django 1.2
========================
@@ -428,15 +436,15 @@ added support for comparison operators. No longer will you have to type:
.. code-block:: html+django
{% ifnotequal a b %}
- ...
+ ...
{% endifnotequal %}
You can now do this::
.. code-block:: html+django
{% if a != b %}
- ...
+ ...
{% endif %}
There's really no reason to use ``{% ifequal %}`` or ``{% ifnotequal %}``
Oops, something went wrong.

0 comments on commit 53b61d9

Please sign in to comment.