4

given a Python class hierarchy, say

class Base:
    def method1
    def method2
    def method3
    ...
class Derived1(Base)
class Derived2(Base)

etc.

and given that Base defines some common interface which is re-implemented differently in each Derived class. What would be the most straightforward way to unit test all member functions of all classes. Since i'm having a class hierarchy, i thought of doing something like...

class MyTestCase(unittest.TestCase):
    def testMethod1:
        ## Just as an example. Can be similar
        self.failUnless(self.instance.method1())
    def testMethod2
    ...

And then setting up the test suite with test cases for different instantiations of the class hierarchy But how do i get the "instance" parameter into the MyTestCase class?

1
  • Great, someone made me the owner of this question which i had created under a different account by error. Many thanks to whomever it did! Commented Jul 3, 2012 at 12:00

2 Answers 2

2

The skip decorator applies to all of the subclasses as well as the base class so you cannot simply tell unittest to skip the base. The simplest thing is probably to not have the base test class derive from TestCase at all:

import unittest

class Base: pass
class Derived1(Base): pass

class BaseTest(object):
    cls = None

    def setUp(self):
        self.instance = self.cls()

    def test_Method1(self):
        print("run test_Method1, cls={}".format(self.cls))

class TestParent(BaseTest, unittest.TestCase):
    cls = Base

class TestDerived1(BaseTest, unittest.TestCase):
    cls = Derived1

unittest.main()

The equivalent code using pytest would be:

class Base: pass
class Derived1(Base): pass

class BaseTest(object):
    cls = None

    def __init__(self):
        self.instance = self.cls()

    def test_Method1(self):
        print("run test_Method1, cls={}".format(self.cls))

class TestParent(BaseTest):
    cls = Base

class TestDerived1(BaseTest):
    cls = Derived1

pytest by default looks in files with a test_ prefix for functions with a test_ prefix or classes with a Test prefix. In the test classes it looks for methods with the test_ prefix.

So it should all just work as desired if converted to use pytest.

Alternatively with pytest you could parametrize test functions with the classes to be tested but that's probably a bigger change. Code like this would run the test once for each class listed in the instance fixture params:

import pytest

@pytest.fixture(params=[Base, Derived1])
def instance(request: FixtureRequest) -> Base:
    cls: Base = request.param
    yield cls()

def test_something(instance: Base) -> None:
    assert instance.method() == 42 # or whatever
Sign up to request clarification or add additional context in comments.

6 Comments

Ok, thanks. Is there any difference to overriding the setUp method for each TestCase subclass? I mean i could as well write: ' class TestDerived1(unittest.TestCase, BaseTest): def setUp(self): self.cls = Derived1' This way it BaseTest could still inherit from TestCase.
You can override setUp or any other methods you wish in the subclasses. I don't see though why you think overriding setUp would help, if BaseTest subclasses TestCase then any tests it contains will still run: if you want some re-usable tests then putting them in a completely isolated means you can choose exactly when to include them: you could for example write tests against an interface and pull in for each test class exactly the right group of interfaces for that class.
+1. This is perfectly fine except we'll get hundreds of warnings/errors from type-checkers and also IDEs won't be able to hint unittest.TestCase's assert* methods in parent as it doesn't inherit from TestCase. Any solution to that?
@S.B that wasn't an issue when I wrote my answer back in 2012. I'd suggest using pytest but if that isn't an option: if TYPE_CHECKING: base = TestCase else: base = object followed by class BaseTest(base): ... should let it hint correctly.
@S.B to clarify my pytest comment, pytest will run tests named test_... when they are inside classes named Test... so for this case will automatically ignore the test methods in BaseTest but run them in the derived class. And type hinting works if you correctly annotate all fixtures.
|
1

You can build the same tests hierarchy

class BaseTest()
    cls = None

    def setUp(self):
        self.instance = self.cls()

    def test_Method1(self) 
        ...

class TestParent(unittest.TestCase, BaseTest):
    cls = Base


class TestDerived1(unittest.TestCase, BaseTest):
    cls = Derived1

...

2 Comments

No, as Scrontch noted the decorator applies not just to the BaseTest class but to the derived ones also.
@Duncan Yeah, that was my fault. BaseTest must not be a subclass of testCase. That's all.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.