Removed django.test.simple and django.test._doctest per deprecation timeline.
refs #17365, #17366, #18727.
This commit is contained in:
parent
b71f183d2e
commit
bf5430a20b
File diff suppressed because it is too large
Load Diff
|
@ -1,251 +0,0 @@
|
||||||
"""
|
|
||||||
This module is pending deprecation as of Django 1.6 and will be removed in
|
|
||||||
version 1.8.
|
|
||||||
|
|
||||||
"""
|
|
||||||
from importlib import import_module
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
import unittest as real_unittest
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
from django.apps import apps
|
|
||||||
from django.test import _doctest as doctest
|
|
||||||
from django.test import runner
|
|
||||||
from django.test.utils import compare_xml, strip_quotes
|
|
||||||
# django.utils.unittest is deprecated, but so is django.test.simple,
|
|
||||||
# and the latter will be removed before the former.
|
|
||||||
from django.utils import unittest
|
|
||||||
from django.utils.deprecation import RemovedInDjango18Warning
|
|
||||||
from django.utils.module_loading import module_has_submodule
|
|
||||||
|
|
||||||
__all__ = ('DjangoTestSuiteRunner',)
|
|
||||||
|
|
||||||
warnings.warn(
|
|
||||||
"The django.test.simple module and DjangoTestSuiteRunner are deprecated; "
|
|
||||||
"use django.test.runner.DiscoverRunner instead.",
|
|
||||||
RemovedInDjango18Warning)
|
|
||||||
|
|
||||||
# The module name for tests outside models.py
|
|
||||||
TEST_MODULE = 'tests'
|
|
||||||
|
|
||||||
|
|
||||||
normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
|
|
||||||
normalize_decimals = lambda s: re.sub(r"Decimal\('(\d+(\.\d*)?)'\)",
|
|
||||||
lambda m: "Decimal(\"%s\")" % m.groups()[0], s)
|
|
||||||
|
|
||||||
|
|
||||||
class OutputChecker(doctest.OutputChecker):
|
|
||||||
def check_output(self, want, got, optionflags):
|
|
||||||
"""
|
|
||||||
The entry method for doctest output checking. Defers to a sequence of
|
|
||||||
child checkers
|
|
||||||
"""
|
|
||||||
checks = (self.check_output_default,
|
|
||||||
self.check_output_numeric,
|
|
||||||
self.check_output_xml,
|
|
||||||
self.check_output_json)
|
|
||||||
for check in checks:
|
|
||||||
if check(want, got, optionflags):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def check_output_default(self, want, got, optionflags):
|
|
||||||
"""
|
|
||||||
The default comparator provided by doctest - not perfect, but good for
|
|
||||||
most purposes
|
|
||||||
"""
|
|
||||||
return doctest.OutputChecker.check_output(self, want, got, optionflags)
|
|
||||||
|
|
||||||
def check_output_numeric(self, want, got, optionflags):
|
|
||||||
"""Doctest does an exact string comparison of output, which means that
|
|
||||||
some numerically equivalent values aren't equal. This check normalizes
|
|
||||||
* long integers (22L) so that they equal normal integers. (22)
|
|
||||||
* Decimals so that they are comparable, regardless of the change
|
|
||||||
made to __repr__ in Python 2.6.
|
|
||||||
"""
|
|
||||||
return doctest.OutputChecker.check_output(self,
|
|
||||||
normalize_decimals(normalize_long_ints(want)),
|
|
||||||
normalize_decimals(normalize_long_ints(got)),
|
|
||||||
optionflags)
|
|
||||||
|
|
||||||
def check_output_xml(self, want, got, optionsflags):
|
|
||||||
try:
|
|
||||||
return compare_xml(want, got)
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def check_output_json(self, want, got, optionsflags):
|
|
||||||
"""
|
|
||||||
Tries to compare want and got as if they were JSON-encoded data
|
|
||||||
"""
|
|
||||||
want, got = strip_quotes(want, got)
|
|
||||||
try:
|
|
||||||
want_json = json.loads(want)
|
|
||||||
got_json = json.loads(got)
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
return want_json == got_json
|
|
||||||
|
|
||||||
|
|
||||||
class DocTestRunner(doctest.DocTestRunner):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
doctest.DocTestRunner.__init__(self, *args, **kwargs)
|
|
||||||
self.optionflags = doctest.ELLIPSIS
|
|
||||||
|
|
||||||
|
|
||||||
doctestOutputChecker = OutputChecker()
|
|
||||||
|
|
||||||
|
|
||||||
def get_tests(app_config):
|
|
||||||
try:
|
|
||||||
test_module = import_module('%s.%s' % (app_config.name, TEST_MODULE))
|
|
||||||
except ImportError:
|
|
||||||
# Couldn't import tests.py. Was it due to a missing file, or
|
|
||||||
# due to an import error in a tests.py that actually exists?
|
|
||||||
if not module_has_submodule(app_config.module, TEST_MODULE):
|
|
||||||
test_module = None
|
|
||||||
else:
|
|
||||||
# The module exists, so there must be an import error in the test
|
|
||||||
# module itself.
|
|
||||||
raise
|
|
||||||
return test_module
|
|
||||||
|
|
||||||
|
|
||||||
def make_doctest(module):
|
|
||||||
return doctest.DocTestSuite(module,
|
|
||||||
checker=doctestOutputChecker,
|
|
||||||
runner=DocTestRunner)
|
|
||||||
|
|
||||||
|
|
||||||
def build_suite(app_config):
|
|
||||||
"""
|
|
||||||
Create a complete Django test suite for the provided application module.
|
|
||||||
"""
|
|
||||||
suite = unittest.TestSuite()
|
|
||||||
|
|
||||||
# Load unit and doctests in the models.py module. If module has
|
|
||||||
# a suite() method, use it. Otherwise build the test suite ourselves.
|
|
||||||
models_module = app_config.models_module
|
|
||||||
if models_module:
|
|
||||||
if hasattr(models_module, 'suite'):
|
|
||||||
suite.addTest(models_module.suite())
|
|
||||||
else:
|
|
||||||
suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(
|
|
||||||
models_module))
|
|
||||||
try:
|
|
||||||
suite.addTest(make_doctest(models_module))
|
|
||||||
except ValueError:
|
|
||||||
# No doc tests in models.py
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Check to see if a separate 'tests' module exists parallel to the
|
|
||||||
# models module
|
|
||||||
tests_module = get_tests(app_config)
|
|
||||||
if tests_module:
|
|
||||||
# Load unit and doctests in the tests.py module. If module has
|
|
||||||
# a suite() method, use it. Otherwise build the test suite ourselves.
|
|
||||||
if hasattr(tests_module, 'suite'):
|
|
||||||
suite.addTest(tests_module.suite())
|
|
||||||
else:
|
|
||||||
suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(
|
|
||||||
tests_module))
|
|
||||||
try:
|
|
||||||
suite.addTest(make_doctest(tests_module))
|
|
||||||
except ValueError:
|
|
||||||
# No doc tests in tests.py
|
|
||||||
pass
|
|
||||||
return suite
|
|
||||||
|
|
||||||
|
|
||||||
def build_test(label):
|
|
||||||
"""
|
|
||||||
Construct a test case with the specified label. Label should be of the
|
|
||||||
form app_label.TestClass or app_label.TestClass.test_method. Returns an
|
|
||||||
instantiated test or test suite corresponding to the label provided.
|
|
||||||
"""
|
|
||||||
parts = label.split('.')
|
|
||||||
if len(parts) < 2 or len(parts) > 3:
|
|
||||||
raise ValueError("Test label '%s' should be of the form app.TestCase "
|
|
||||||
"or app.TestCase.test_method" % label)
|
|
||||||
|
|
||||||
app_config = apps.get_app_config(parts[0])
|
|
||||||
models_module = app_config.models_module
|
|
||||||
tests_module = get_tests(app_config)
|
|
||||||
|
|
||||||
test_modules = []
|
|
||||||
if models_module:
|
|
||||||
test_modules.append(models_module)
|
|
||||||
if tests_module:
|
|
||||||
test_modules.append(tests_module)
|
|
||||||
|
|
||||||
TestClass = None
|
|
||||||
for module in test_modules:
|
|
||||||
TestClass = getattr(models_module, parts[1], None)
|
|
||||||
if TestClass is not None:
|
|
||||||
break
|
|
||||||
|
|
||||||
try:
|
|
||||||
if issubclass(TestClass, (unittest.TestCase, real_unittest.TestCase)):
|
|
||||||
if len(parts) == 2: # label is app.TestClass
|
|
||||||
try:
|
|
||||||
return unittest.TestLoader().loadTestsFromTestCase(
|
|
||||||
TestClass)
|
|
||||||
except TypeError:
|
|
||||||
raise ValueError(
|
|
||||||
"Test label '%s' does not refer to a test class"
|
|
||||||
% label)
|
|
||||||
else: # label is app.TestClass.test_method
|
|
||||||
return TestClass(parts[2])
|
|
||||||
except TypeError:
|
|
||||||
# TestClass isn't a TestClass - it must be a method or normal class
|
|
||||||
pass
|
|
||||||
|
|
||||||
#
|
|
||||||
# If there isn't a TestCase, look for a doctest that matches
|
|
||||||
#
|
|
||||||
tests = []
|
|
||||||
for module in test_modules:
|
|
||||||
try:
|
|
||||||
doctests = make_doctest(module)
|
|
||||||
# Now iterate over the suite, looking for doctests whose name
|
|
||||||
# matches the pattern that was given
|
|
||||||
for test in doctests:
|
|
||||||
if test._dt_test.name in (
|
|
||||||
'%s.%s' % (module.__name__, '.'.join(parts[1:])),
|
|
||||||
'%s.__test__.%s' % (
|
|
||||||
module.__name__, '.'.join(parts[1:]))):
|
|
||||||
tests.append(test)
|
|
||||||
except ValueError:
|
|
||||||
# No doctests found.
|
|
||||||
pass
|
|
||||||
|
|
||||||
# If no tests were found, then we were given a bad test label.
|
|
||||||
if not tests:
|
|
||||||
raise ValueError("Test label '%s' does not refer to a test" % label)
|
|
||||||
|
|
||||||
# Construct a suite out of the tests that matched.
|
|
||||||
return unittest.TestSuite(tests)
|
|
||||||
|
|
||||||
|
|
||||||
class DjangoTestSuiteRunner(runner.DiscoverRunner):
|
|
||||||
|
|
||||||
def build_suite(self, test_labels, extra_tests=None, **kwargs):
|
|
||||||
suite = unittest.TestSuite()
|
|
||||||
|
|
||||||
if test_labels:
|
|
||||||
for label in test_labels:
|
|
||||||
if '.' in label:
|
|
||||||
suite.addTest(build_test(label))
|
|
||||||
else:
|
|
||||||
app_config = apps.get_app_config(label)
|
|
||||||
suite.addTest(build_suite(app_config))
|
|
||||||
else:
|
|
||||||
for app_config in apps.get_app_configs():
|
|
||||||
suite.addTest(build_suite(app_config))
|
|
||||||
|
|
||||||
if extra_tests:
|
|
||||||
for test in extra_tests:
|
|
||||||
suite.addTest(test)
|
|
||||||
|
|
||||||
return runner.reorder_suite(suite, (unittest.TestCase,))
|
|
|
@ -2067,11 +2067,6 @@ Default: ``'django.test.runner.DiscoverRunner'``
|
||||||
The name of the class to use for starting the test suite. See
|
The name of the class to use for starting the test suite. See
|
||||||
:ref:`other-testing-frameworks`.
|
:ref:`other-testing-frameworks`.
|
||||||
|
|
||||||
.. versionchanged:: 1.6
|
|
||||||
|
|
||||||
Previously the default ``TEST_RUNNER`` was
|
|
||||||
``django.test.simple.DjangoTestSuiteRunner``.
|
|
||||||
|
|
||||||
.. setting:: THOUSAND_SEPARATOR
|
.. setting:: THOUSAND_SEPARATOR
|
||||||
|
|
||||||
THOUSAND_SEPARATOR
|
THOUSAND_SEPARATOR
|
||||||
|
|
|
@ -3,7 +3,7 @@ doc_files = docs extras AUTHORS INSTALL LICENSE README.rst
|
||||||
install-script = scripts/rpm-install.sh
|
install-script = scripts/rpm-install.sh
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
exclude=build,.git,./django/utils/dictconfig.py,./django/utils/unittest.py,./django/utils/lru_cache.py,./tests/comment_tests/*,./django/test/_doctest.py,./django/utils/six.py,./django/conf/app_template/*,./django/dispatch/weakref_backports.py
|
exclude=build,.git,./django/utils/dictconfig.py,./django/utils/unittest.py,./django/utils/lru_cache.py,./django/utils/six.py,./django/conf/app_template/*,./django/dispatch/weakref_backports.py
|
||||||
ignore=E128,E501,W601
|
ignore=E128,E501,W601
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
|
|
|
@ -4,7 +4,6 @@ Tests for django test runner
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
import types
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
@ -12,7 +11,7 @@ from django.core.management import call_command
|
||||||
from django import db
|
from django import db
|
||||||
from django.test import runner, TestCase, TransactionTestCase, skipUnlessDBFeature
|
from django.test import runner, TestCase, TransactionTestCase, skipUnlessDBFeature
|
||||||
from django.test.testcases import connections_support_transactions
|
from django.test.testcases import connections_support_transactions
|
||||||
from django.test.utils import IgnoreAllDeprecationWarningsMixin, override_system_checks
|
from django.test.utils import override_system_checks
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
from admin_scripts.tests import AdminScriptTestCase
|
from admin_scripts.tests import AdminScriptTestCase
|
||||||
|
@ -221,27 +220,6 @@ class Ticket17477RegressionTests(AdminScriptTestCase):
|
||||||
self.assertNoOutput(err)
|
self.assertNoOutput(err)
|
||||||
|
|
||||||
|
|
||||||
class ModulesTestsPackages(IgnoreAllDeprecationWarningsMixin, unittest.TestCase):
|
|
||||||
|
|
||||||
def test_get_tests(self):
|
|
||||||
"Check that the get_tests helper function can find tests in a directory"
|
|
||||||
from django.apps import AppConfig
|
|
||||||
from django.test.simple import get_tests
|
|
||||||
app_config = AppConfig.create('test_runner.valid_app')
|
|
||||||
app_config.import_models({})
|
|
||||||
tests = get_tests(app_config)
|
|
||||||
self.assertIsInstance(tests, types.ModuleType)
|
|
||||||
|
|
||||||
def test_import_error(self):
|
|
||||||
"Test for #12658 - Tests with ImportError's shouldn't fail silently"
|
|
||||||
from django.apps import AppConfig
|
|
||||||
from django.test.simple import get_tests
|
|
||||||
app_config = AppConfig.create('test_runner_invalid_app')
|
|
||||||
app_config.import_models({})
|
|
||||||
with self.assertRaises(ImportError):
|
|
||||||
get_tests(app_config)
|
|
||||||
|
|
||||||
|
|
||||||
class Sqlite3InMemoryTestDbs(TestCase):
|
class Sqlite3InMemoryTestDbs(TestCase):
|
||||||
|
|
||||||
available_apps = []
|
available_apps = []
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
import unittest
|
|
||||||
|
|
||||||
from django.apps import apps
|
|
||||||
from django.test.utils import IgnoreAllDeprecationWarningsMixin
|
|
||||||
|
|
||||||
|
|
||||||
def suite():
|
|
||||||
testSuite = unittest.TestSuite()
|
|
||||||
testSuite.addTest(SuiteOverrideTest('test_suite_override'))
|
|
||||||
return testSuite
|
|
||||||
|
|
||||||
|
|
||||||
class SuiteOverrideTest(IgnoreAllDeprecationWarningsMixin, unittest.TestCase):
|
|
||||||
|
|
||||||
def test_suite_override(self):
|
|
||||||
"""
|
|
||||||
Validate that you can define a custom suite when running tests with
|
|
||||||
``django.test.simple.DjangoTestSuiteRunner`` (which builds up a test
|
|
||||||
suite using ``build_suite``).
|
|
||||||
"""
|
|
||||||
|
|
||||||
from django.test.simple import build_suite
|
|
||||||
app_config = apps.get_app_config("test_suite_override")
|
|
||||||
suite = build_suite(app_config)
|
|
||||||
self.assertEqual(suite.countTestCases(), 1)
|
|
||||||
|
|
||||||
|
|
||||||
class SampleTests(unittest.TestCase):
|
|
||||||
"""These tests should not be discovered, due to the custom suite."""
|
|
||||||
def test_one(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_two(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_three(self):
|
|
||||||
pass
|
|
|
@ -1,77 +0,0 @@
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
__test__ = {"API_TEST": r"""
|
|
||||||
# Some checks of the doctest output normalizer.
|
|
||||||
# Standard doctests do fairly
|
|
||||||
>>> import json
|
|
||||||
>>> from django.utils.xmlutils import SimplerXMLGenerator
|
|
||||||
>>> from django.utils.six import StringIO
|
|
||||||
|
|
||||||
>>> def produce_json():
|
|
||||||
... return json.dumps(['foo', {'bar': ('baz', None, 1.0, 2), 'whiz': 42}])
|
|
||||||
|
|
||||||
>>> def produce_xml():
|
|
||||||
... stream = StringIO()
|
|
||||||
... xml = SimplerXMLGenerator(stream, encoding='utf-8')
|
|
||||||
... xml.startDocument()
|
|
||||||
... xml.startElement("foo", {"aaa" : "1.0", "bbb": "2.0"})
|
|
||||||
... xml.startElement("bar", {"ccc" : "3.0"})
|
|
||||||
... xml.characters("Hello")
|
|
||||||
... xml.endElement("bar")
|
|
||||||
... xml.startElement("whiz", {})
|
|
||||||
... xml.characters("Goodbye")
|
|
||||||
... xml.endElement("whiz")
|
|
||||||
... xml.endElement("foo")
|
|
||||||
... xml.endDocument()
|
|
||||||
... return stream.getvalue()
|
|
||||||
|
|
||||||
>>> def produce_xml_fragment():
|
|
||||||
... stream = StringIO()
|
|
||||||
... xml = SimplerXMLGenerator(stream, encoding='utf-8')
|
|
||||||
... xml.startElement("foo", {"aaa": "1.0", "bbb": "2.0"})
|
|
||||||
... xml.characters("Hello")
|
|
||||||
... xml.endElement("foo")
|
|
||||||
... xml.startElement("bar", {"ccc": "3.0", "ddd": "4.0"})
|
|
||||||
... xml.endElement("bar")
|
|
||||||
... return stream.getvalue()
|
|
||||||
|
|
||||||
# JSON output is normalized for field order, so it doesn't matter
|
|
||||||
# which order json dictionary attributes are listed in output
|
|
||||||
>>> produce_json()
|
|
||||||
'["foo", {"bar": ["baz", null, 1.0, 2], "whiz": 42}]'
|
|
||||||
|
|
||||||
>>> produce_json()
|
|
||||||
'["foo", {"whiz": 42, "bar": ["baz", null, 1.0, 2]}]'
|
|
||||||
|
|
||||||
# XML output is normalized for attribute order, so it doesn't matter
|
|
||||||
# which order XML element attributes are listed in output
|
|
||||||
>>> produce_xml()
|
|
||||||
'<?xml version="1.0" encoding="UTF-8"?>\n<foo aaa="1.0" bbb="2.0"><bar ccc="3.0">Hello</bar><whiz>Goodbye</whiz></foo>'
|
|
||||||
|
|
||||||
>>> produce_xml()
|
|
||||||
'<?xml version="1.0" encoding="UTF-8"?>\n<foo bbb="2.0" aaa="1.0"><bar ccc="3.0">Hello</bar><whiz>Goodbye</whiz></foo>'
|
|
||||||
|
|
||||||
>>> produce_xml_fragment()
|
|
||||||
'<foo aaa="1.0" bbb="2.0">Hello</foo><bar ccc="3.0" ddd="4.0"></bar>'
|
|
||||||
|
|
||||||
>>> produce_xml_fragment()
|
|
||||||
'<foo bbb="2.0" aaa="1.0">Hello</foo><bar ddd="4.0" ccc="3.0"></bar>'
|
|
||||||
|
|
||||||
"""}
|
|
||||||
|
|
||||||
if six.PY2:
|
|
||||||
__test__["API_TEST"] += """
|
|
||||||
>>> def produce_long():
|
|
||||||
... return 42L
|
|
||||||
|
|
||||||
>>> def produce_int():
|
|
||||||
... return 42
|
|
||||||
|
|
||||||
# Long values are normalized and are comparable to normal integers ...
|
|
||||||
>>> produce_long()
|
|
||||||
42
|
|
||||||
|
|
||||||
# ... and vice versa
|
|
||||||
>>> produce_int()
|
|
||||||
42L
|
|
||||||
"""
|
|
|
@ -11,8 +11,7 @@ from django.http import HttpResponse
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.test import SimpleTestCase, TestCase, skipIfDBFeature, skipUnlessDBFeature
|
from django.test import SimpleTestCase, TestCase, skipIfDBFeature, skipUnlessDBFeature
|
||||||
from django.test.html import HTMLParseError, parse_html
|
from django.test.html import HTMLParseError, parse_html
|
||||||
from django.test.utils import (CaptureQueriesContext,
|
from django.test.utils import CaptureQueriesContext, override_settings
|
||||||
IgnoreAllDeprecationWarningsMixin, override_settings)
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
from .models import Person
|
from .models import Person
|
||||||
|
@ -623,15 +622,6 @@ class AssertFieldOutputTests(SimpleTestCase):
|
||||||
self.assertFieldOutput(MyCustomField, {}, {}, empty_value=None)
|
self.assertFieldOutput(MyCustomField, {}, {}, empty_value=None)
|
||||||
|
|
||||||
|
|
||||||
class DoctestNormalizerTest(IgnoreAllDeprecationWarningsMixin, SimpleTestCase):
|
|
||||||
|
|
||||||
def test_normalizer(self):
|
|
||||||
from django.test.simple import make_doctest
|
|
||||||
suite = make_doctest("test_utils.doctest_output")
|
|
||||||
failures = unittest.TextTestRunner(stream=six.StringIO()).run(suite)
|
|
||||||
self.assertEqual(failures.failures, [])
|
|
||||||
|
|
||||||
|
|
||||||
# for OverrideSettingsTests
|
# for OverrideSettingsTests
|
||||||
def fake_view(request):
|
def fake_view(request):
|
||||||
pass
|
pass
|
||||||
|
|
Loading…
Reference in New Issue