Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How use Python unittest to test a class hierarchy?

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?

like image 270
Scrontch Avatar asked Oct 23 '25 15:10

Scrontch


2 Answers

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
like image 143
Duncan Avatar answered Oct 25 '25 04:10

Duncan


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

...
like image 29
Maksym Polshcha Avatar answered Oct 25 '25 04:10

Maksym Polshcha



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!