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.