Grouping tests in pytest: Classes vs plain functions

This answer presents two compelling use-cases for a TestClass in pytest:

  • Joint parametrization of multiple test methods belonging to a given class.
  • Reuse of test data and test logic via subclass inheritance

Joint parametrization of multiple test methods belonging to a given class.

The pytest parametrization decorator, @pytest.mark.parametrize, can be used to make inputs available to multiple methods within a class. In the code below, the inputs param1 and param2 are available to each of the methods TestGroup.test_one and TestGroup.test_two.

"""test_class_parametrization.py"""
import pytest

@pytest.mark.parametrize(
    ("param1", "param2"),
    [
        ("a", "b"),
        ("c", "d"),
    ],
)
class TestGroup:
    """A class with common parameters, `param1` and `param2`."""

    @pytest.fixture
    def fixt(self) -> int:
        """This fixture will only be available within the scope of TestGroup"""
        return 123

    def test_one(self, param1: str, param2: str, fixt: int) -> None:
        print("\ntest_one", param1, param2, fixt)

    def test_two(self, param1: str, param2: str) -> None:
        print("\ntest_two", param1, param2)
$ pytest -s test_class_parametrization.py
================================================================== test session starts ==================================================================
platform linux -- Python 3.8.6, pytest-6.2.1, py-1.10.0, pluggy-0.13.1
rootdir: /home/jbss
plugins: pylint-0.18.0
collected 4 items

test_class_parametrization.py
test_one a b 123
.
test_one c d 123
.
test_two a b
.
test_two c d
.

=================================================================== 4 passed in 0.01s ===================================================================

Reuse of test data and test logic via subclass inheritance

I’ll use a modified version of code taken from another answer to demonstrate the usefulness of inheriting class attributes/methods from TestClass to TestSubclass:

# in file `test_example.py`
class TestClass:
    VAR: int = 3
    DATA: int = 4

    def test_var_positive(self) -> None:
        assert self.VAR >= 0


class TestSubclass(TestClass):
    VAR: int = 8

    def test_var_even(self) -> None:
        assert self.VAR % 2 == 0

    def test_data(self) -> None:
        assert self.DATA == 4

Running pytest on this file causes four tests to be run:

$ pytest -v test_example.py
=========== test session starts ===========
platform linux -- Python 3.8.2, pytest-5.4.2, py-1.8.1
collected 4 items

test_example.py::TestClass::test_var_positive PASSED
test_example.py::TestSubclass::test_var_positive PASSED
test_example.py::TestSubclass::test_var_even PASSED
test_example.py::TestSubclass::test_data PASSED

In the subclass, the inherited test_var_positive method is run using the updated value self.VAR == 8, and the newly defined test_data method is run against the inherited attribute self.DATA == 4. Such method and attribute inheritance gives a flexible way to re-use or modify shared functionality between different groups of test-cases.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)