From 197e6f484a5f3816fe911c78e89fcc70249faee1 Mon Sep 17 00:00:00 2001 From: Eugene Toder Date: Sat, 17 Aug 2024 12:15:14 -0400 Subject: [PATCH] Fix using parameterized_class with mixins Extend the fix for #73 to include inherited methods: copy all test methods from the original class into generated classes and set them to None in the original class. This ensures that the original class does not run by itself. Fixes #119 --- parameterized/parameterized.py | 19 +++++++++----- parameterized/test.py | 48 +++++++++++++++++++++++++++------- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/parameterized/parameterized.py b/parameterized/parameterized.py index 56dc535..a9063c4 100644 --- a/parameterized/parameterized.py +++ b/parameterized/parameterized.py @@ -687,9 +687,16 @@ class TestUserAccessLevel(TestCase): def decorator(base_class): test_class_module = sys.modules[base_class.__module__].__dict__ + # Copy all test methods into generated classes because we are going to set + # them to None in the base class (see below). + test_methods = { + method_name: getattr(base_class, method_name) + for method_name in dir(base_class) + if method_name.startswith("test") + } + base_dict = {**base_class.__dict__, **test_methods} for idx, input_dict in enumerate(input_dicts): - test_class_dict = dict(base_class.__dict__) - test_class_dict.update(input_dict) + test_class_dict = {**base_dict, **input_dict} name = class_name_func(base_class, idx, input_dict) @@ -699,11 +706,9 @@ def decorator(base_class): # leave the test_ methods in place, the test runner will try to pick # them up and run them... which doesn't make sense, since no parameters # will have been applied. - # Address this by iterating over the base class and remove all test - # methods. - for method_name in list(base_class.__dict__): - if method_name.startswith("test"): - delattr(base_class, method_name) + # Address this by setting all test methods in the base class to None. + for method_name in test_methods: + setattr(base_class, method_name, None) return base_class return decorator diff --git a/parameterized/test.py b/parameterized/test.py index 6c71f79..396db7f 100644 --- a/parameterized/test.py +++ b/parameterized/test.py @@ -35,7 +35,7 @@ def assert_raises_regexp_decorator(expected_exception, expected_regexp): def func_decorator(func): @wraps(func) def wrapper(self, *args, **kwargs): - with self.assertRaisesRegexp(expected_exception, expected_regexp): + with self.assertRaisesRegex(expected_exception, expected_regexp): func(self, *args, **kwargs) return wrapper @@ -615,12 +615,28 @@ def test_method(self): )) +class BaseTestCase(TestCase): + def setUp(self): + missing_tests.remove("%s:setUp(%r, %r)" %( + self.__class__.__name__, + self.foo, + self.bar, + )) + + def tearDown(self): + missing_tests.remove("%s:tearDown(%r, %r)" %( + self.__class__.__name__, + self.foo, + self.bar, + )) + + @parameterized_class([ {"foo": 42}, {"bar": "some stuff"}, {"bar": "other stuff", "name": "some name", "foo": 12}, ]) -class TestParameterizedClassDict(TestCase): +class TestParameterizedClassDict(BaseTestCase): expect([ "TestParameterizedClassDict_0:setUp(42, 'empty')", "TestParameterizedClassDict_0:test_method(42, 'empty')", @@ -639,21 +655,26 @@ class TestParameterizedClassDict(TestCase): def setUp(self): # Ensure that super() works (issue #73) super(TestParameterizedClassDict, self).setUp() - missing_tests.remove("%s:setUp(%r, %r)" %( - self.__class__.__name__, - self.foo, - self.bar, - )) def tearDown(self): # Ensure that super() works (issue #73) - super(TestParameterizedClassDict, self).tearDown() - missing_tests.remove("%s:tearDown(%r, %r)" %( + super().tearDown() + + def test_method(self): + missing_tests.remove("%s:test_method(%r, %r)" %( self.__class__.__name__, self.foo, self.bar, )) + +class TestMixin: + def setUp(self): + super(TestMixin, self).setUp() + + def tearDown(self): + super().tearDown() + def test_method(self): missing_tests.remove("%s:test_method(%r, %r)" %( self.__class__.__name__, @@ -662,6 +683,15 @@ def test_method(self): )) +@parameterized_class([{"foo": 42, "bar": 21}]) +class TestParameterizedMixin(TestMixin, BaseTestCase): + expect([ + "TestParameterizedMixin_0:setUp(42, 21)", + "TestParameterizedMixin_0:test_method(42, 21)", + "TestParameterizedMixin_0:tearDown(42, 21)", + ]) + + class TestUnicodeDocstring(object): @parameterized.expand([ 'value1',