Sometimes tests need to use a resource that takes a long time to set up. Sharing this resource helps tests run faster and saves effort.
Shared expensive resource patterns in PyTest
Start learning this pattern below
Jump into concepts and practice - no test required
or
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Introduction
Syntax
PyTest
import pytest @pytest.fixture(scope="module") def expensive_resource(): print("Setting up resource") resource = "Expensive setup" yield resource print("Tearing down resource")
Use scope="module" or scope="session" to share the resource across tests.
The yield keyword lets you run setup code before tests and cleanup code after.
Examples
PyTest
import pytest @pytest.fixture(scope="session") def db_connection(): print("Connect to DB") conn = "DB Connection" yield conn print("Close DB Connection")
PyTest
import pytest @pytest.fixture(scope="module") def web_server(): print("Start server") server = "Running server" yield server print("Stop server")
Sample Program
This example shows two tests sharing one expensive resource setup. The setup runs once before both tests, and cleanup runs after both finish.
PyTest
import pytest @pytest.fixture(scope="module") def expensive_resource(): print("Setup expensive resource") resource = "Resource Ready" yield resource print("Cleanup expensive resource") def test_one(expensive_resource): print(f"Test one uses {expensive_resource}") assert expensive_resource == "Resource Ready" def test_two(expensive_resource): print(f"Test two uses {expensive_resource}") assert expensive_resource == "Resource Ready"
Important Notes
Sharing resources reduces test time but be careful to avoid tests changing shared data.
Use scope="function" if you want a fresh resource for each test.
Always clean up resources to avoid side effects on other tests.
Summary
Use pytest fixtures with scopes to share expensive resources.
Setup runs once per scope, saving time.
Cleanup runs after all tests using the resource finish.
Practice
1. What is the main benefit of using a pytest fixture with a
scope='module' when dealing with expensive resources?easy
Solution
Step 1: Understand fixture scopes in pytest
Fixtures withscope='module'run once per module, not per test function.Step 2: Relate scope to expensive resource usage
Running setup once per module saves time by avoiding repeated expensive setups for each test.Final Answer:
The fixture setup runs once per module, reducing repeated expensive setup. -> Option BQuick Check:
Module scope = setup once per module [OK]
Hint: Module scope runs setup once per module, saving time [OK]
Common Mistakes:
- Confusing module scope with function scope
- Thinking setup runs before every test
- Assuming cleanup runs immediately after each test
2. Which of the following is the correct syntax to define a pytest fixture that sets up a database connection once per test session?
easy
Solution
Step 1: Identify scope for once per test session
Thesessionscope runs the fixture setup once for the entire test session.Step 2: Match syntax with correct scope
@pytest.fixture(scope='session')\ndef db_conn():\n pass uses@pytest.fixture(scope='session'), which is correct for this purpose.Final Answer:
@pytest.fixture(scope='session')\ndef db_conn():\n pass -> Option AQuick Check:
Session scope = setup once per session [OK]
Hint: Session scope means setup runs once per entire test run [OK]
Common Mistakes:
- Using function scope for expensive shared resources
- Confusing module and session scopes
- Forgetting to specify scope in fixture decorator
3. Given the following pytest fixture and test code, what will be the output when running the tests?
@pytest.fixture(scope='module')
def resource():
print('Setup resource')
yield
print('Cleanup resource')
def test_one(resource):
print('Test one running')
def test_two(resource):
print('Test two running')medium
Solution
Step 1: Understand module scope fixture behavior
Withscope='module', setup runs once before any tests in the module, and cleanup runs after all tests finish.Step 2: Trace the print statements during test execution
First, 'Setup resource' prints. Then 'Test one running' and 'Test two running' print during tests. Finally, 'Cleanup resource' prints after all tests.Final Answer:
Setup resource\nTest one running\nTest two running\nCleanup resource -> Option AQuick Check:
Module scope = setup once before all tests, cleanup after all [OK]
Hint: Module scope runs setup once before all tests, cleanup after all [OK]
Common Mistakes:
- Expecting cleanup after each test
- Thinking setup runs before each test
- Ignoring yield behavior in fixture
4. Identify the error in this pytest fixture code that aims to share a resource across tests in a class:
@pytest.fixture(scope='class')
def setup_resource():
resource = open('file.txt')
yield resource
resource.close()
def test_example(setup_resource):
assert setup_resource.readable()medium
Solution
Step 1: Review fixture resource setup and cleanup
The fixture opens a file and yields it, then closes it after tests.Step 2: Check for error handling in setup
If opening the file fails, no exception handling is present, which can cause test failures or resource leaks.Final Answer:
The fixture does not handle exceptions during resource setup. -> Option CQuick Check:
Missing exception handling in fixture setup = problem [OK]
Hint: Always handle exceptions in fixture setup to avoid leaks [OK]
Common Mistakes:
- Confusing fixture scope requirements
- Thinking @pytest.mark.usefixtures is mandatory
- Assuming fixture name mismatch causes error
5. You want to share a database connection across multiple test classes but ensure it resets after all tests finish. Which pytest fixture pattern correctly achieves this?
hard
Solution
Step 1: Determine scope for sharing across multiple test classes
Sharing across classes requires at leastsessionscope to cover all tests.Step 2: Ensure resource resets after all tests finish
Usingyieldallows cleanup code after tests; callingconn.reset()beforeconn.close()resets the connection properly.Final Answer:
@pytest.fixture(scope='session')\ndef db_conn():\n conn = connect_db()\n yield conn\n conn.reset()\n conn.close() -> Option DQuick Check:
Session scope + yield cleanup with reset = correct pattern [OK]
Hint: Use session scope and yield cleanup to reset shared resource [OK]
Common Mistakes:
- Using too narrow scope like function or class
- Forgetting to reset resource before closing
- Not using yield to separate setup and cleanup
