Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parameterized_class doesn't remove test methods from the base class's superclasses #119

Open
leifwalsh opened this issue Mar 17, 2021 · 4 comments · May be fixed by #180
Open

parameterized_class doesn't remove test methods from the base class's superclasses #119

leifwalsh opened this issue Mar 17, 2021 · 4 comments · May be fixed by #180

Comments

@leifwalsh
Copy link

If I have a parameterized test class whose test methods are inherited:

class FooTestMethods:
    def test_foo(self):
        ...


@parameterized_class(...)
class FooTests(FooTestMethods, unittest.TestCase):
    pass

Then it appears that while #73 attempts to remove all the test* methods from the base class FooTests that it leaves in the module, it doesn't remove them from the whole MRO hierarchy. Therefore, I'm left with a FooTests object with test methods that will be discovered and run, but they might assume the parameterized values will be set, when they actually aren't.

I'm not totally sure what to do about this, maybe the base class's test methods should all be replaced with a noop method?

@JohnnyDeuss
Copy link

JohnnyDeuss commented Apr 27, 2021

I've run into this exact issue as well. I've currently worked around it by adding a setUpClass method to skip the unparameterized test case.

@parameterized_class(...)
class FooTests(FooTestMethods, unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        if cls == FooTests:
            raise unittest.SkipTest("`parameterized_class` bug")
        super().setUpClass()

@zachlowry
Copy link

zachlowry commented Apr 18, 2022

Currently struggling with this as well. The workaround from @JohnnyDeuss has been helpful but I'd prefer a more generic solution.

class FooTestMethods
    @classmethod
    def setUpClass(cls, parameterized_cls):
        if cls is parameterized_cls:
            raise SkipTest("skipping test for non-parameterized class")

@parameterized_class(...)
class FooTests(FooTestMethods, unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass(FooTests)

@eltoder
Copy link

eltoder commented Feb 13, 2023

@wolever I think the cleanest way to solve this is to change the API of @parameterized_class and ask users to not inherit the decorated class from unittest.TestCase. parameterized_class will add this inheritance to the classes that it generates. For backward compatibility, if the class is already inherited from TestCase, we can either keep your logic of deleting "test_" methods1 or instead rebuild the class and exclude TestCase from bases. The former has the downside that cases reported in this bug will not work. The latter has the downside that super() calls with implicit arguments will not work, but cases from this bug and super with explicit arguments will work. Either way, we'll only have issues in the "backward compatibility" mode. With the new approach of not inheriting the class from TestCase, everything will work.

What do you think?

Footnotes

  1. Note that this code is not strictly correct. The default prefix is "test" rather than "test_" and it is configurable at runtime: source code.

@sshishov
Copy link

I feel to fix the issue all-together, we have to duplicate (create with indices) not only the decorated class, but also all custom MRO classes. In this case we can easily remove the base classes and not have that patch with removing test_ prefixes.

Currently: unittest.TestCase -> BaseClass -> ChildClass

Transformed into:

  • unittest.TestCase -> BaseClass -> ChildClass_0
  • unittest.TestCase -> BaseClass -> ChildClass_1

Instead it should be transformed into:

  • unittest.TestCase -> BaseClass_0 -> ChildClass_0
  • unittest.TestCase -> BaseClass_1 -> ChildClass_1

In this case the chain is not common and we can manipulate with it.

What do you think guys? I just do not know if it is possible to achieve this

@eltoder @zachlowry @JohnnyDeuss @wolever

eltoder added a commit to eltoder/parameterized that referenced this issue Aug 18, 2024
Extend the fix for wolever#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 wolever#119
@eltoder eltoder linked a pull request Aug 18, 2024 that will close this issue
eltoder added a commit to eltoder/parameterized that referenced this issue Aug 18, 2024
Extend the fix for wolever#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 wolever#119
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants