Python > Testing in Python > pytest > Fixtures in pytest
Basic Pytest Fixture Example
This example demonstrates a simple pytest fixture to provide a pre-configured database connection for test functions.
Code Snippet
The @pytest.fixture
decorator transforms the db_connection
function into a fixture. The fixture's purpose is to set up a resource (in this case, a simulated database connection) before the test runs and tear it down afterward. The yield
statement separates the setup and teardown phases. The test_db_operations
function then receives this database connection as an argument, allowing it to perform tests that rely on a database. Output is printed to demonstrate setup and teardown.
import pytest
@pytest.fixture
def db_connection():
# Setup: Establish a database connection
conn = connect_to_database()
print("\nSetup: Connected to database")
yield conn # Provide the connection to the tests
# Teardown: Close the connection after the tests are done
conn.close()
print("\nTeardown: Closed database connection")
def connect_to_database():
# Simulate a database connection
class MockConnection:
def close(self):
pass # Simulate closing connection
return MockConnection()
def test_db_operations(db_connection):
# Test function that uses the db_connection fixture
print("\nRunning test with database connection")
assert db_connection is not None
# Example Usage (Run with pytest)
Concepts Behind the Snippet
Fixtures in pytest are functions that run before each test function to which they are applied. They are used to provide a fixed baseline so tests are repeatable and reliable. Fixtures handle setup and teardown, managing resources like database connections, temporary files, or mocked objects. The yield
keyword within the fixture allows you to define code that will be executed after the test has completed, enabling proper cleanup. Fixtures promote code reuse and reduce boilerplate within test functions.
Real-Life Use Case Section
Imagine testing a web application. A fixture could be used to create a test user in the database, log that user in, and then clean up the user's account after the test completes. Another use case involves mocking external API calls. A fixture can mock the API and return predefined responses, allowing tests to run quickly and reliably without depending on the external service's availability. This is crucial for integration tests where isolating dependencies is vital.
Best Practices
Interview Tip
When discussing pytest fixtures in an interview, be prepared to explain the benefits of using fixtures over traditional setup/teardown methods. Emphasize code reuse, readability, and the ability to parameterize fixtures for different test scenarios. Give concrete examples from your past projects where fixtures simplified your testing process.
When to Use Them
Use fixtures when you need to perform setup or teardown tasks before or after tests. This includes things like initializing databases, creating temporary files, mocking external services, or setting up test data. Fixtures are most valuable when the same setup/teardown logic is needed across multiple tests.
Memory Footprint
The memory footprint of a fixture depends on the resources it manages. Small fixtures that create simple objects will have a minimal impact. However, fixtures that deal with large datasets or complex objects can consume significant memory. Consider using smaller scopes (e.g., 'function' instead of 'session') for fixtures that are memory-intensive to release resources more frequently. Proper teardown is crucial to prevent memory leaks.
Alternatives
Before pytest introduced fixtures, unittest
used setUp
and tearDown
methods. While these still work, fixtures offer greater flexibility and readability. For very simple setup/teardown tasks, you might also consider inline setup within the test function itself, but this quickly becomes unmanageable for complex scenarios.
Pros
Cons
FAQ
-
What's the difference between
yield
andreturn
in a pytest fixture?
yield
is used to separate the setup and teardown phases of a fixture. The code beforeyield
is executed before the test, and the code afteryield
is executed after the test, regardless of whether the test passes or fails.return
would only allow for code to be executed before the test. -
How do I specify the scope of a fixture?
You can specify the scope using thescope
parameter in the@pytest.fixture
decorator. For example:@pytest.fixture(scope="module")
. Common scopes include 'function' (default), 'class', 'module', 'package', and 'session'.