diff --git a/AUTHORS b/AUTHORS index b1fa5456b6..4f7ef379f6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -832,6 +832,7 @@ answer newbie questions, and generally made Django that much better: Wiktor KoƂodziej Wiley Kestner Wiliam Alves de Souza + Will Ayd William Schwartz Will Hardy Wilson Miner diff --git a/django/test/utils.py b/django/test/utils.py index 1a1b78f34d..cbefaf0e09 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -831,6 +831,9 @@ class isolate_apps(TestContextDecorator): def tag(*tags): """Decorator to add tags to a test class or method.""" def decorator(obj): - setattr(obj, 'tags', set(tags)) + if hasattr(obj, 'tags'): + obj.tags = obj.tags.union(tags) + else: + setattr(obj, 'tags', set(tags)) return obj return decorator diff --git a/docs/spelling_wordlist b/docs/spelling_wordlist index 34754bde76..20508bcdfd 100644 --- a/docs/spelling_wordlist +++ b/docs/spelling_wordlist @@ -695,6 +695,7 @@ subviews subwidget subwidgets superclass +superclasses superset swappable symlink diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt index d349efe7a0..65d1fb463b 100644 --- a/docs/topics/testing/tools.txt +++ b/docs/topics/testing/tools.txt @@ -1619,6 +1619,25 @@ You can also tag a test case:: class SampleTestCase(TestCase): ... +Subclasses inherit tags from superclasses, and methods inherit tags from their +class. Given:: + + @tag('foo') + class SampleTestCaseChild(SampleTestCase): + + @tag('bar') + def test(self): + ... + +``SampleTestCaseChild.test`` will be labeled with ``'slow'``, ``'core'``, +``'bar'``, and ``'foo'``. + +.. versionchanged:: 2.1 + + In older versions, tagged tests don't inherit tags from classes, and + tagged subclasses don't inherit tags from superclasses. For example, + ``SampleTestCaseChild.test`` is labeled only with ``'bar'``. + Then you can choose which tests to run. For example, to run only fast tests: .. code-block:: console diff --git a/tests/test_runner/test_discover_runner.py b/tests/test_runner/test_discover_runner.py index d897344d16..5efcbe4f5d 100644 --- a/tests/test_runner/test_discover_runner.py +++ b/tests/test_runner/test_discover_runner.py @@ -198,3 +198,15 @@ class DiscoverRunnerTest(TestCase): self.assertEqual(runner.build_suite(['test_runner_apps.tagged.tests']).countTestCases(), 0) runner = DiscoverRunner(exclude_tags=['slow']) self.assertEqual(runner.build_suite(['test_runner_apps.tagged.tests']).countTestCases(), 0) + + def test_tag_inheritance(self): + def count_tests(**kwargs): + suite = DiscoverRunner(**kwargs).build_suite(['test_runner_apps.tagged.tests_inheritance']) + return suite.countTestCases() + + self.assertEqual(count_tests(tags=['foo']), 4) + self.assertEqual(count_tests(tags=['bar']), 2) + self.assertEqual(count_tests(tags=['baz']), 2) + self.assertEqual(count_tests(tags=['foo'], exclude_tags=['bar']), 2) + self.assertEqual(count_tests(tags=['foo'], exclude_tags=['bar', 'baz']), 1) + self.assertEqual(count_tests(exclude_tags=['foo']), 0) diff --git a/tests/test_runner_apps/tagged/tests_inheritance.py b/tests/test_runner_apps/tagged/tests_inheritance.py new file mode 100644 index 0000000000..59e564202d --- /dev/null +++ b/tests/test_runner_apps/tagged/tests_inheritance.py @@ -0,0 +1,29 @@ +from unittest import TestCase + +from django.test import tag + + +@tag('foo') +class FooBase(TestCase): + pass + + +class Foo(FooBase): + + def test_no_new_tags(self): + pass + + @tag('baz') + def test_new_func_tag(self): + pass + + +@tag('bar') +class FooBar(FooBase): + + def test_new_class_tag_only(self): + pass + + @tag('baz') + def test_new_class_and_func_tags(self): + pass