diff --git a/django/test/runner.py b/django/test/runner.py index 4d4e489cee..7bb95b40a9 100644 --- a/django/test/runner.py +++ b/django/test/runner.py @@ -233,7 +233,7 @@ def reorder_suite(suite, classes): def partition_suite(suite, classes, bins): """ - Partitions a test suite by test type. + Partitions a test suite by test type. Also prevents duplicated tests. classes is a sequence of types bins is a sequence of TestSuites, one more than classes @@ -248,10 +248,12 @@ def partition_suite(suite, classes, bins): else: for i in range(len(classes)): if isinstance(test, classes[i]): - bins[i].addTest(test) + if test not in bins[i]: + bins[i].addTest(test) break else: - bins[-1].addTest(test) + if test not in bins[-1]: + bins[-1].addTest(test) def setup_databases(verbosity, interactive, keepdb=False, **kwargs): diff --git a/tests/test_runner/test_discover_runner.py b/tests/test_runner/test_discover_runner.py index 4ab0cf8ae3..6ed17097ea 100644 --- a/tests/test_runner/test_discover_runner.py +++ b/tests/test_runner/test_discover_runner.py @@ -117,6 +117,15 @@ class DiscoverRunnerTest(TestCase): # All others can follow in unspecified order, including doctests self.assertIn('DocTestCase', [t.__class__.__name__ for t in suite._tests[2:]]) + def test_duplicates_ignored(self): + """ + Tests shouldn't be discovered twice when discovering on overlapping paths. + """ + single = DiscoverRunner().build_suite(["django.contrib.gis"]).countTestCases() + dups = DiscoverRunner().build_suite( + ["django.contrib.gis", "django.contrib.gis.tests.geo3d"]).countTestCases() + self.assertEqual(single, dups) + def test_overrideable_test_suite(self): self.assertEqual(DiscoverRunner().test_suite, TestSuite)