diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index 1585c6983..2164f1381 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -277,7 +277,9 @@ def _classmethod_is_defined_at_leaf(cls, method_name): f = method.__func__ except AttributeError: pytest.fail('%s.%s should be a classmethod' % (cls, method_name)) - if PY2 and not (inspect.ismethod(method) and method.__self__ is cls): + if PY2 and not (inspect.ismethod(method) and + inspect.isclass(method.__self__) and + issubclass(cls, method.__self__)): pytest.fail('%s.%s should be a classmethod' % (cls, method_name)) return f is not super_method.__func__ @@ -290,9 +292,11 @@ def _disable_class_methods(cls): return _disabled_classmethods[cls] = ( - cls.setUpClass, + # want the classmethod object, not the resulting bound method + # otherwise when restoring, the inheritence will be broken + cls.__dict__.get('setUpClass'), _classmethod_is_defined_at_leaf(cls, 'setUpClass'), - cls.tearDownClass, + cls.__dict__.get('tearDownClass'), _classmethod_is_defined_at_leaf(cls, 'tearDownClass'), ) diff --git a/tests/test_unittest.py b/tests/test_unittest.py index 1d951e1d7..d4ecbc381 100644 --- a/tests/test_unittest.py +++ b/tests/test_unittest.py @@ -141,6 +141,79 @@ def test_pass(self): ]) assert result.ret == 1 + def test_setUpClass_multiple_subclasses(self, django_testdir): + django_testdir.create_test_module(''' + from django.test import TestCase + + + class TestFoo(TestCase): + @classmethod + def setUpClass(cls): + super(TestFoo, cls).setUpClass() + + def test_shared(self): + pass + + + class TestBar(TestFoo): + def test_bar1(self): + pass + + + class TestBar2(TestFoo): + def test_bar21(self): + pass + ''') + + result = django_testdir.runpytest_subprocess('-v', '-s') + result.stdout.fnmatch_lines([ + "*TestFoo::test_shared Creating test database for*", + "PASSED", + "*TestBar::test_bar1 PASSED", + "*TestBar::test_shared PASSED", + "*TestBar2::test_bar21 PASSED", + "*TestBar2::test_shared PASSED*", + ]) + assert result.ret == 0 + + def test_setUpClass_skip(self, django_testdir): + django_testdir.create_test_module(''' + from django.test import TestCase + import pytest + + + class TestFoo(TestCase): + @classmethod + def setUpClass(cls): + if cls is TestFoo: + raise pytest.skip("Skip base class") + super(TestFoo, cls).setUpClass() + + def test_shared(self): + pass + + + class TestBar(TestFoo): + def test_bar1(self): + pass + + + class TestBar2(TestFoo): + def test_bar21(self): + pass + ''') + + result = django_testdir.runpytest_subprocess('-v', '-s') + result.stdout.fnmatch_lines([ + "*TestFoo::test_shared Creating test database for*", + "SKIPPED", + "*TestBar::test_bar1 PASSED", + "*TestBar::test_shared PASSED", + "*TestBar2::test_bar21 PASSED", + "*TestBar2::test_shared PASSED*", + ]) + assert result.ret == 0 + def test_multi_inheritance_setUpClass(self, django_testdir): django_testdir.create_test_module(''' from django.test import TestCase