Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test a class' inherited methods in pytest

Tags:

python

pytest

house.py:

class House:
    def is_habitable(self):
        return True

    def is_on_the_ground(self):
        return True

conftest.py:

import pytest
from house import House


@pytest.fixture(scope='class')
def house():
    return House()

test_house.py:

class TestHouse:
    def test_habitability(self, house):
        assert house.is_habitable()

    def test_groundedness(self, house):
        assert house.is_on_the_ground()

Up to that point, everything is being tested.

Now I add a subclass and override a method in house.py:

class House:
    def is_habitable(self):
        return True

    def is_on_the_ground(self):
        return True


class TreeHouse(House):
    def is_on_the_ground(self):
        return False

I also add a new fixture for that class in conftest.py:

import pytest
from house import House
from house import TreeHouse


@pytest.fixture(scope='class')
def house():
    return House()


@pytest.fixture(scope='class')
def tree_house():
    return TreeHouse()

I add a new test class for tree house in test_house.py:

class TestHouse:
    def test_habitability(self, house):
        assert house.is_habitable()

    def test_groundedness(self, house):
        assert house.is_on_the_ground()


class TestTreeHouse:
    def test_groundedness(self, tree_house):
        assert not tree_house.is_on_the_ground()

At that point, the code works, but there are cases that are not tested. For example, to be complete, I would need to test again the methods inherited from House in TreeHouse.

Rewriting the same tests from the TestHouse would not be DRY.

How do I test the inherited method of TreeHouse (in this case is_habitable) without duplicating code?

I would like something like re-testing the TreeHouse with the same tests that its super class runs through, but not for the methods / properties that are new or overridden.

After some research I came across contradictory sources. And after digging in the pytest documentation I fail to understand what applies to this scenario.

I am interested in the pytest way to do this. Please reference the docs and explain how that applies here.

like image 538
Bastian Avatar asked Sep 04 '17 15:09

Bastian


1 Answers

One way to do this would be to use the fixture name house for all test methods (even if it's testing a TreeHouse), and override its value in each test context:

class TestTreeHouse(TestHouse):
    @pytest.fixture
    def house(self, tree_house):
        return tree_house

    def test_groundedness(self, house):
        assert not house.is_on_the_ground()

Also note TestTreeHouse inherits from TestHouse. Since pytest merely enumerates methods of classes (i.e. there is no "registration" done with, say, a @pytest.test() decorator), all tests defined in TestHouse will be discovered in subclasses of it, without any further intervention.

like image 187
theY4Kman Avatar answered Oct 14 '22 16:10

theY4Kman