diff --git a/changelog/4700.bugfix.rst b/changelog/4700.bugfix.rst new file mode 100644 index 000000000..3f8acb876 --- /dev/null +++ b/changelog/4700.bugfix.rst @@ -0,0 +1,2 @@ +Fix regression where ``setUpClass`` would always be called in subclasses even if all tests +were skipped by a ``unittest.skip()`` decorator applied in the subclass. diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index e00636d46..58d79845b 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -87,6 +87,9 @@ def _make_xunit_fixture(obj, setup_name, teardown_name, scope, pass_self): @pytest.fixture(scope=scope, autouse=True) def fixture(self, request): + if getattr(self, "__unittest_skip__", None): + reason = self.__unittest_skip_why__ + pytest.skip(reason) if setup is not None: if pass_self: setup(self, request.function) diff --git a/testing/example_scripts/unittest/test_setup_skip.py b/testing/example_scripts/unittest/test_setup_skip.py new file mode 100644 index 000000000..93f79bb3b --- /dev/null +++ b/testing/example_scripts/unittest/test_setup_skip.py @@ -0,0 +1,13 @@ +"""Skipping an entire subclass with unittest.skip() should *not* call setUp from a base class.""" +import unittest + + +class Base(unittest.TestCase): + def setUp(self): + assert 0 + + +@unittest.skip("skip all tests") +class Test(Base): + def test_foo(self): + assert 0 diff --git a/testing/example_scripts/unittest/test_setup_skip_class.py b/testing/example_scripts/unittest/test_setup_skip_class.py new file mode 100644 index 000000000..4f251dcba --- /dev/null +++ b/testing/example_scripts/unittest/test_setup_skip_class.py @@ -0,0 +1,14 @@ +"""Skipping an entire subclass with unittest.skip() should *not* call setUpClass from a base class.""" +import unittest + + +class Base(unittest.TestCase): + @classmethod + def setUpClass(cls): + assert 0 + + +@unittest.skip("skip all tests") +class Test(Base): + def test_foo(self): + assert 0 diff --git a/testing/example_scripts/unittest/test_setup_skip_module.py b/testing/example_scripts/unittest/test_setup_skip_module.py new file mode 100644 index 000000000..98befbe51 --- /dev/null +++ b/testing/example_scripts/unittest/test_setup_skip_module.py @@ -0,0 +1,12 @@ +"""setUpModule is always called, even if all tests in the module are skipped""" +import unittest + + +def setUpModule(): + assert 0 + + +@unittest.skip("skip all tests") +class Base(unittest.TestCase): + def test(self): + assert 0 diff --git a/testing/test_unittest.py b/testing/test_unittest.py index 2c60cd271..fe33855fa 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -1026,3 +1026,18 @@ def test_error_message_with_parametrized_fixtures(testdir): "*Function type: TestCaseFunction", ] ) + + +@pytest.mark.parametrize( + "test_name, expected_outcome", + [ + ("test_setup_skip.py", "1 skipped"), + ("test_setup_skip_class.py", "1 skipped"), + ("test_setup_skip_module.py", "1 error"), + ], +) +def test_setup_inheritance_skipping(testdir, test_name, expected_outcome): + """Issue #4700""" + testdir.copy_example("unittest/{}".format(test_name)) + result = testdir.runpytest() + result.stdout.fnmatch_lines("* {} in *".format(expected_outcome))