Fixed #30245 -- Added -k option to DiscoverRunner.
This commit is contained in:
parent
719b746620
commit
568eed9e79
|
@ -17,6 +17,7 @@ from django.test.utils import (
|
||||||
teardown_databases as _teardown_databases, teardown_test_environment,
|
teardown_databases as _teardown_databases, teardown_test_environment,
|
||||||
)
|
)
|
||||||
from django.utils.datastructures import OrderedSet
|
from django.utils.datastructures import OrderedSet
|
||||||
|
from django.utils.version import PY37
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import tblib.pickling_support
|
import tblib.pickling_support
|
||||||
|
@ -407,7 +408,7 @@ class DiscoverRunner:
|
||||||
def __init__(self, pattern=None, top_level=None, verbosity=1,
|
def __init__(self, pattern=None, top_level=None, verbosity=1,
|
||||||
interactive=True, failfast=False, keepdb=False,
|
interactive=True, failfast=False, keepdb=False,
|
||||||
reverse=False, debug_mode=False, debug_sql=False, parallel=0,
|
reverse=False, debug_mode=False, debug_sql=False, parallel=0,
|
||||||
tags=None, exclude_tags=None, **kwargs):
|
tags=None, exclude_tags=None, test_name_patterns=None, **kwargs):
|
||||||
|
|
||||||
self.pattern = pattern
|
self.pattern = pattern
|
||||||
self.top_level = top_level
|
self.top_level = top_level
|
||||||
|
@ -421,6 +422,14 @@ class DiscoverRunner:
|
||||||
self.parallel = parallel
|
self.parallel = parallel
|
||||||
self.tags = set(tags or [])
|
self.tags = set(tags or [])
|
||||||
self.exclude_tags = set(exclude_tags or [])
|
self.exclude_tags = set(exclude_tags or [])
|
||||||
|
self.test_name_patterns = None
|
||||||
|
if test_name_patterns:
|
||||||
|
# unittest does not export the _convert_select_pattern function
|
||||||
|
# that converts command-line arguments to patterns.
|
||||||
|
self.test_name_patterns = {
|
||||||
|
pattern if '*' in pattern else '*%s*' % pattern
|
||||||
|
for pattern in test_name_patterns
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_arguments(cls, parser):
|
def add_arguments(cls, parser):
|
||||||
|
@ -433,7 +442,7 @@ class DiscoverRunner:
|
||||||
help='The test matching pattern. Defaults to test*.py.',
|
help='The test matching pattern. Defaults to test*.py.',
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-k', '--keepdb', action='store_true',
|
'--keepdb', action='store_true',
|
||||||
help='Preserves the test DB between runs.'
|
help='Preserves the test DB between runs.'
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -461,6 +470,15 @@ class DiscoverRunner:
|
||||||
'--exclude-tag', action='append', dest='exclude_tags',
|
'--exclude-tag', action='append', dest='exclude_tags',
|
||||||
help='Do not run tests with the specified tag. Can be used multiple times.',
|
help='Do not run tests with the specified tag. Can be used multiple times.',
|
||||||
)
|
)
|
||||||
|
if PY37:
|
||||||
|
parser.add_argument(
|
||||||
|
'-k', action='append', dest='test_name_patterns',
|
||||||
|
help=(
|
||||||
|
'Only run test methods and classes that match the pattern '
|
||||||
|
'or substring. Can be used multiple times. Same as '
|
||||||
|
'unittest -k option.'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def setup_test_environment(self, **kwargs):
|
def setup_test_environment(self, **kwargs):
|
||||||
setup_test_environment(debug=self.debug_mode)
|
setup_test_environment(debug=self.debug_mode)
|
||||||
|
@ -470,6 +488,7 @@ class DiscoverRunner:
|
||||||
suite = self.test_suite()
|
suite = self.test_suite()
|
||||||
test_labels = test_labels or ['.']
|
test_labels = test_labels or ['.']
|
||||||
extra_tests = extra_tests or []
|
extra_tests = extra_tests or []
|
||||||
|
self.test_loader.testNamePatterns = self.test_name_patterns
|
||||||
|
|
||||||
discover_kwargs = {}
|
discover_kwargs = {}
|
||||||
if self.pattern is not None:
|
if self.pattern is not None:
|
||||||
|
|
|
@ -1360,7 +1360,7 @@ The ``test`` command receives options on behalf of the specified
|
||||||
:option:`--testrunner`. These are the options of the default test runner:
|
:option:`--testrunner`. These are the options of the default test runner:
|
||||||
:class:`~django.test.runner.DiscoverRunner`.
|
:class:`~django.test.runner.DiscoverRunner`.
|
||||||
|
|
||||||
.. django-admin-option:: --keepdb, -k
|
.. django-admin-option:: --keepdb
|
||||||
|
|
||||||
Preserves the test database between test runs. This has the advantage of
|
Preserves the test database between test runs. This has the advantage of
|
||||||
skipping both the create and destroy actions which can greatly decrease the
|
skipping both the create and destroy actions which can greatly decrease the
|
||||||
|
@ -1438,6 +1438,18 @@ May be specified multiple times and combined with :option:`test --exclude-tag`.
|
||||||
Excludes tests :ref:`marked with the specified tags <topics-tagging-tests>`.
|
Excludes tests :ref:`marked with the specified tags <topics-tagging-tests>`.
|
||||||
May be specified multiple times and combined with :option:`test --tag`.
|
May be specified multiple times and combined with :option:`test --tag`.
|
||||||
|
|
||||||
|
.. django-admin-option:: -k TEST_NAME_PATTERNS
|
||||||
|
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
|
||||||
|
Runs test methods and classes matching test name patterns, in the same way as
|
||||||
|
:option:`unittest's -k option<unittest.-k>`. Can be specified multiple times.
|
||||||
|
|
||||||
|
.. admonition:: Python 3.7 and later
|
||||||
|
|
||||||
|
This feature is only available for Python 3.7 and later.
|
||||||
|
|
||||||
|
|
||||||
``testserver``
|
``testserver``
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|
|
@ -238,6 +238,9 @@ Tests
|
||||||
attribute :attr:`~django.test.Response.exc_info`, a tuple providing
|
attribute :attr:`~django.test.Response.exc_info`, a tuple providing
|
||||||
information of the exception that occurred.
|
information of the exception that occurred.
|
||||||
|
|
||||||
|
* Tests and test cases to run can be selected by test name pattern using the
|
||||||
|
new :option:`test -k` option.
|
||||||
|
|
||||||
URLs
|
URLs
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
|
@ -360,6 +363,9 @@ Miscellaneous
|
||||||
This converts ``'`` to ``'`` instead of the previous equivalent decimal
|
This converts ``'`` to ``'`` instead of the previous equivalent decimal
|
||||||
code ``'``.
|
code ``'``.
|
||||||
|
|
||||||
|
* The ``django-admin test -k`` option now works as the :option:`unittest
|
||||||
|
-k<unittest.-k>` option rather than as a shortcut for ``--keepdb``.
|
||||||
|
|
||||||
.. _deprecated-features-3.0:
|
.. _deprecated-features-3.0:
|
||||||
|
|
||||||
Features deprecated in 3.0
|
Features deprecated in 3.0
|
||||||
|
|
|
@ -424,7 +424,7 @@ behavior. This class defines the ``run_tests()`` entry point, plus a
|
||||||
selection of other methods that are used to by ``run_tests()`` to set up,
|
selection of other methods that are used to by ``run_tests()`` to set up,
|
||||||
execute and tear down the test suite.
|
execute and tear down the test suite.
|
||||||
|
|
||||||
.. class:: DiscoverRunner(pattern='test*.py', top_level=None, verbosity=1, interactive=True, failfast=False, keepdb=False, reverse=False, debug_mode=False, debug_sql=False, **kwargs)
|
.. class:: DiscoverRunner(pattern='test*.py', top_level=None, verbosity=1, interactive=True, failfast=False, keepdb=False, reverse=False, debug_mode=False, debug_sql=False, test_name_patterns=None, **kwargs)
|
||||||
|
|
||||||
``DiscoverRunner`` will search for tests in any file matching ``pattern``.
|
``DiscoverRunner`` will search for tests in any file matching ``pattern``.
|
||||||
|
|
||||||
|
@ -463,6 +463,9 @@ execute and tear down the test suite.
|
||||||
as the traceback. If ``verbosity`` is ``2``, then queries in all tests are
|
as the traceback. If ``verbosity`` is ``2``, then queries in all tests are
|
||||||
output.
|
output.
|
||||||
|
|
||||||
|
``test_name_patterns`` can be used to specify a set of patterns for
|
||||||
|
filtering test methods and classes by their names.
|
||||||
|
|
||||||
Django may, from time to time, extend the capabilities of the test runner
|
Django may, from time to time, extend the capabilities of the test runner
|
||||||
by adding new arguments. The ``**kwargs`` declaration allows for this
|
by adding new arguments. The ``**kwargs`` declaration allows for this
|
||||||
expansion. If you subclass ``DiscoverRunner`` or write your own test
|
expansion. If you subclass ``DiscoverRunner`` or write your own test
|
||||||
|
|
|
@ -28,6 +28,7 @@ else:
|
||||||
RemovedInDjango31Warning, RemovedInDjango40Warning,
|
RemovedInDjango31Warning, RemovedInDjango40Warning,
|
||||||
)
|
)
|
||||||
from django.utils.log import DEFAULT_LOGGING
|
from django.utils.log import DEFAULT_LOGGING
|
||||||
|
from django.utils.version import PY37
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
|
@ -271,7 +272,8 @@ class ActionSelenium(argparse.Action):
|
||||||
|
|
||||||
|
|
||||||
def django_tests(verbosity, interactive, failfast, keepdb, reverse,
|
def django_tests(verbosity, interactive, failfast, keepdb, reverse,
|
||||||
test_labels, debug_sql, parallel, tags, exclude_tags):
|
test_labels, debug_sql, parallel, tags, exclude_tags,
|
||||||
|
test_name_patterns):
|
||||||
state = setup(verbosity, test_labels, parallel)
|
state = setup(verbosity, test_labels, parallel)
|
||||||
extra_tests = []
|
extra_tests = []
|
||||||
|
|
||||||
|
@ -290,6 +292,7 @@ def django_tests(verbosity, interactive, failfast, keepdb, reverse,
|
||||||
parallel=actual_test_processes(parallel),
|
parallel=actual_test_processes(parallel),
|
||||||
tags=tags,
|
tags=tags,
|
||||||
exclude_tags=exclude_tags,
|
exclude_tags=exclude_tags,
|
||||||
|
test_name_patterns=test_name_patterns,
|
||||||
)
|
)
|
||||||
failures = test_runner.run_tests(
|
failures = test_runner.run_tests(
|
||||||
test_labels or get_installed(),
|
test_labels or get_installed(),
|
||||||
|
@ -416,7 +419,7 @@ if __name__ == "__main__":
|
||||||
help='Tells Django to stop running the test suite after first failed test.',
|
help='Tells Django to stop running the test suite after first failed test.',
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-k', '--keepdb', action='store_true',
|
'--keepdb', action='store_true',
|
||||||
help='Tells Django to preserve the test database between runs.',
|
help='Tells Django to preserve the test database between runs.',
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -469,6 +472,14 @@ if __name__ == "__main__":
|
||||||
'--exclude-tag', dest='exclude_tags', action='append',
|
'--exclude-tag', dest='exclude_tags', action='append',
|
||||||
help='Do not run tests with the specified tag. Can be used multiple times.',
|
help='Do not run tests with the specified tag. Can be used multiple times.',
|
||||||
)
|
)
|
||||||
|
if PY37:
|
||||||
|
parser.add_argument(
|
||||||
|
'-k', dest='test_name_patterns', action='append',
|
||||||
|
help=(
|
||||||
|
'Only run test methods and classes matching test name pattern. '
|
||||||
|
'Same as unittest -k option. Can be used multiple times.'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
options = parser.parse_args()
|
options = parser.parse_args()
|
||||||
|
|
||||||
|
@ -507,6 +518,7 @@ if __name__ == "__main__":
|
||||||
options.keepdb, options.reverse, options.modules,
|
options.keepdb, options.reverse, options.modules,
|
||||||
options.debug_sql, options.parallel, options.tags,
|
options.debug_sql, options.parallel, options.tags,
|
||||||
options.exclude_tags,
|
options.exclude_tags,
|
||||||
|
getattr(options, 'test_name_patterns', None),
|
||||||
)
|
)
|
||||||
if failures:
|
if failures:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import os
|
import os
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from unittest import TestSuite, TextTestRunner, defaultTestLoader
|
from unittest import TestSuite, TextTestRunner, defaultTestLoader, skipUnless
|
||||||
|
|
||||||
from django.db import connections
|
from django.db import connections
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
from django.test.runner import DiscoverRunner
|
from django.test.runner import DiscoverRunner
|
||||||
from django.test.utils import captured_stdout
|
from django.test.utils import captured_stdout
|
||||||
|
from django.utils.version import PY37
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
|
@ -23,6 +24,13 @@ def change_cwd(directory):
|
||||||
|
|
||||||
class DiscoverRunnerTests(SimpleTestCase):
|
class DiscoverRunnerTests(SimpleTestCase):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_test_methods_names(suite):
|
||||||
|
return [
|
||||||
|
t.__class__.__name__ + '.' + t._testMethodName
|
||||||
|
for t in suite._tests
|
||||||
|
]
|
||||||
|
|
||||||
def test_init_debug_mode(self):
|
def test_init_debug_mode(self):
|
||||||
runner = DiscoverRunner()
|
runner = DiscoverRunner()
|
||||||
self.assertFalse(runner.debug_mode)
|
self.assertFalse(runner.debug_mode)
|
||||||
|
@ -71,6 +79,34 @@ class DiscoverRunnerTests(SimpleTestCase):
|
||||||
|
|
||||||
self.assertEqual(count, 1)
|
self.assertEqual(count, 1)
|
||||||
|
|
||||||
|
@skipUnless(PY37, 'unittest -k option requires Python 3.7 and later')
|
||||||
|
def test_name_patterns(self):
|
||||||
|
all_test_1 = [
|
||||||
|
'DjangoCase1.test_1', 'DjangoCase2.test_1',
|
||||||
|
'SimpleCase1.test_1', 'SimpleCase2.test_1',
|
||||||
|
'UnittestCase1.test_1', 'UnittestCase2.test_1',
|
||||||
|
]
|
||||||
|
all_test_2 = [
|
||||||
|
'DjangoCase1.test_2', 'DjangoCase2.test_2',
|
||||||
|
'SimpleCase1.test_2', 'SimpleCase2.test_2',
|
||||||
|
'UnittestCase1.test_2', 'UnittestCase2.test_2',
|
||||||
|
]
|
||||||
|
all_tests = sorted([*all_test_1, *all_test_2, 'UnittestCase2.test_3_test'])
|
||||||
|
for pattern, expected in [
|
||||||
|
[['test_1'], all_test_1],
|
||||||
|
[['UnittestCase1'], ['UnittestCase1.test_1', 'UnittestCase1.test_2']],
|
||||||
|
[['*test'], ['UnittestCase2.test_3_test']],
|
||||||
|
[['test*'], all_tests],
|
||||||
|
[['test'], all_tests],
|
||||||
|
[['test_1', 'test_2'], sorted([*all_test_1, *all_test_2])],
|
||||||
|
[['test*1'], all_test_1],
|
||||||
|
]:
|
||||||
|
with self.subTest(pattern):
|
||||||
|
suite = DiscoverRunner(
|
||||||
|
test_name_patterns=pattern
|
||||||
|
).build_suite(['test_runner_apps.simple'])
|
||||||
|
self.assertEqual(expected, self.get_test_methods_names(suite))
|
||||||
|
|
||||||
def test_file_path(self):
|
def test_file_path(self):
|
||||||
with change_cwd(".."):
|
with change_cwd(".."):
|
||||||
count = DiscoverRunner().build_suite(
|
count = DiscoverRunner().build_suite(
|
||||||
|
@ -170,7 +206,7 @@ class DiscoverRunnerTests(SimpleTestCase):
|
||||||
msg="Methods of Django cases should be reversed.")
|
msg="Methods of Django cases should be reversed.")
|
||||||
self.assertIn('test_2', suite[4].id(),
|
self.assertIn('test_2', suite[4].id(),
|
||||||
msg="Methods of simple cases should be reversed.")
|
msg="Methods of simple cases should be reversed.")
|
||||||
self.assertIn('test_2', suite[8].id(),
|
self.assertIn('test_2', suite[9].id(),
|
||||||
msg="Methods of unittest cases should be reversed.")
|
msg="Methods of unittest cases should be reversed.")
|
||||||
|
|
||||||
def test_overridable_get_test_runner_kwargs(self):
|
def test_overridable_get_test_runner_kwargs(self):
|
||||||
|
|
|
@ -55,3 +55,6 @@ class UnittestCase2(TestCase):
|
||||||
|
|
||||||
def test_2(self):
|
def test_2(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_3_test(self):
|
||||||
|
pass
|
||||||
|
|
Loading…
Reference in New Issue