How to Avoid Repeated Instantiation of a Class in Each Pytest Unit Test
The aim of this page📝 is to share how using @pytest.fixture
decorator can help avoid the repetition of class instantiation in Pytest tests. I find it syntactically a bit cumbersome as you access your class through a decorated function, but it's still useful.
2 min readSep 14, 2023
import pytest
import example_module
#ExampleClass defined in example_module
@pytest.fixture
def client():
return example_module.ExampleClass(TEST_CLIENT_TAG, TEST_TICKET_ID)
def test_class_contains_expected_data(client):
assert client.client_tag == "com_foobar"
assert client.org_id == "bbc19d7f-b45b-4dad-b9bd-34a818f23241"
assert client.ticket_id == "EXAMPLE-1234"
assert client.dev_server == "com-foobar-dev1.net"
assert client.prod_server == "com-foobar-prod1.net"
def test_read_keys_returns_dict_with_dev_and_prod_readonly_keys(client, monkeypatch):
def mock_get_keys(_, __):
return {
"dev": "123456789",
"prod": "123456789"
}
monkeypatch.setattr(ExampleClass, "_get_keys", mock_get_keys)
assert client.read_only_keys == {
"dev": "123456789",
"prod": "123456789"
}
- … and so on for the rest of the test functions
- In this example, we define a
client
fixture that returns an instance of the class. - Then, we can pass this fixture as an argument to each test function that needs it.
- This way, the
ExampleClass
class is only instantiated once and reused across all test functions. - In pytest, you can use fixtures to set up preconditions for your tests.
- Fixtures are defined using the
@pytest.fixture
decorator and can take parameters. - Here’s a simpler example that demonstrates how to initialize a class with two arguments before running tests that check the values of its fields:
import pytest
class MyClass:
def __init__(self, name, age):
self.name = name
self.age = age
@pytest.fixture
def my_class():
return MyClass(name="pavol", age=39)
def test_name(my_class):
assert my_class.name == "pavol"
def test_age(my_class):
assert my_class.age == 39
- Please note that the following, a more intuitive approach of “class fixtures” is not supported (maybe in the future, the error message says:)
""" DOES NOT WORK """
@pytest.fixture
class AccessConfig:
def __init__(self):
self.client_name = "com_snowplowanalytics"
self.env = "prod1"
# > ..\..\..\..# \AppData\Local\Programs\Python\Python311\Lib\site-packages\_pytest\fixtures.# py:1195: in __call__
# raise ValueError("class fixtures not supported (maybe in the future)")
# E ValueError: class fixtures not supported (maybe in the future)