I've recently started using pytest for testing in Python and created a fixture to manage a collection of items using gRPC. Below is the code snippet for my fixture:
import pytest
@pytest.fixture(scope="session")
def collection():
grpc_page = GrpcPages().collections
def create_collection(collection_id=None, **kwargs):
default_params = {
"id": collection_id,
"is_active": True,
# some other params
}
try:
return grpc_page.create_collection(**{**default_params, **kwargs})
except Exception as err:
print(err)
raise err
yield create_collection
def delete_created_collection():
# Some code to hard and soft delete created data
This is my first attempt at creating a fixture, and I realized that I need a mechanism to delete data created during the fixture's lifecycle.
While exploring options for implementing teardown procedures, I came across yield and addfinalizer. From what I understand, both can be used to define teardown actions in pytest fixtures. However, I'm having trouble finding clear documentation and examples that explain the key differences between these two approaches and when to choose one over the other.
Here are the questions (for fast-forwarding :) ):
The main difference is not the number of addfinalizer
or fixture
s, there is no difference at all. You can add as many as you want (or just have more than one operation in on of them)
@pytest.fixture(scope='session', autouse=True)
def fixture_one():
print('fixture_one setup')
yield
print('fixture_one teardown')
@pytest.fixture(scope='session', autouse=True)
def fixture_two():
print('fixture_two setup')
yield
print('fixture_two teardown')
def test_one():
print('test_one')
Output
example.py::test_one
fixture_one setup
fixture_two setup
PASSED [100%]test_one
fixture_two teardown
fixture_one teardown
The main difference is if the teardown will run in case of a failure in the setup stage. This is useful if there is need for cleanup even if the setup failed.
Without finalizer the teardown won't run if there was an exception in the setup
@pytest.fixture(scope='session', autouse=True)
def fixture_one():
print('fixture_one setup')
raise Exception('Error')
yield
print('fixture_one teardown')
def test_one():
print('test_one')
Output
ERROR [100%]
fixture_one setup
test setup failed
...
E Exception: Error
example.py:8: Exception
But with finalizer it will
@pytest.fixture(scope='session', autouse=True)
def fixture_one(request):
def finalizer():
print('fixture_one teardown')
request.addfinalizer(finalizer)
print('fixture_one setup')
raise Exception('Error')
yield
Output
ERROR [100%]
fixture_one setup
test setup failed
...
E Exception: Error
example.py:13: Exception
fixture_one teardown