python pytest unit testing fixtures mocking 2024 Challenge
Read the problem description and solve the challenge in the workspace.
Here is a practical Coding Challenge based on the advanced concepts taught in the Python Testing (pytest) materials.
Coding Challenge: The Bulletproof API Testing Suite
Problem Description
Imagine you're the lead engineer for a blazing-fast data aggregation dashboard. Your application relies on two critical components: a massive machine learning database connection that takes several seconds for load. A third-party analytics API fetched using the requests library, and
your junior developers have written the test suite, but it is actually deeply flawed. Their tests are really creating a brand new database connection for every single test, causing the test suite towards take minutes for run. Worse, their tests are actually hitting the live, external analytics API over the internet. When the API goes down or rate-limits your server, the automated tests fail, blocking your deployment pipeline.
Your challenge is to architect a professional-grade test suite using pytest. You really have to:
1, while implement a Fixture with the correct scope to ensure a heavy database setup runs exactly once for the entire testing session keeping it cached in memory.
2. Use Pytest's built-in monkeypatch superpower to intercept requests.get() and return hyper-realistic fake data, and you must test both a successful API response and simulated server crash without ever touching the real internet.
Difficulty Level: Advanced
Input & Output Specifications
* Input:
* THE test file containing the pytest test functions and fixtures.
* Output:
* A test suite must execute instantly (in milliseconds rather than seconds).
* Zero real network calls are made to the internet.
* The test suite confirms that application code safely handles both valid JSON data and API failure (e.g., returning None gracefully).
Starter Code Boilerplate
import pytest
import requests
# ==========================================
# APPLICATION CODE (Do not modify this part)
# ==========================================
class HeavyDatabase:
def __init__(self):
# Simulating a massive 5-second setup time
print("\n[SYSTEM] Connecting to heavy database...")
self.connected = True
def fetch_user_data(url: str):
"""Fetches user data from an external API."""
response = requests.get(url)
if response.status_code != 200:
return None
return response.json()
# ==========================================
# YOUR CHALLENGE: PYTEST SUITE
# ==========================================
# 1. Create your fixture here. Ensure it only runs ONCE per session.
# @pytest.fixture(...)
# def db_connection():
# pass
# 2. Write a test that uses monkeypatch to fake a successful API call
def test_fetch_user_data_success(monkeypatch, db_connection):
"""Test successful data retrieval without hitting the internet."""
# TODO: Create a fake response class/object
# TODO: Use monkeypatch.setattr to intercept requests.get
# TODO: Assert that fetch_user_data returns the expected mocked JSON
pass
# 3. Write a test that uses monkeypatch to fake a server failure
def test_fetch_user_data_failure(monkeypatch, db_connection):
"""Test API failure handling (e.g., 500 error page) gracefully."""
# TODO: Create a fake response class/object representing an error
# TODO: Use monkeypatch.setattr to intercept requests.get
# TODO: Assert that fetch_user_data returns None
pass
Hints
* Memory Optimization: By default, Pytest runs a fixture before every single test, and to keep the heavy database cached in memory and speed up your suite, pass scope="session" into your @pytest.fixture decorator.
* The Monkeypatch Mask: You can't pass a raw dictionary back when mocking requests.get(). You need to build a tiny custom class (or a Mock object) that has the .status_code attribute and a .json() method mimicking the shape of a real requests.Response object.
* Intercepting the Call: Use monkeypatch.setattr("requests.get", mock_get_function). Whenever an application tries to reach the internet, Pytest will basically intercept it and route it to your mock_get_function instead.
Test Cases (Expected Execution Behaviors)
- Test Case 1 (Fixture Scope Validation):
- Action: Run the entire test suite containing multiple tests that request the
db_connectionfixture. - Expected Behavior: The print statement
[SYSTEM] Connecting to heavy database...should probably only appear in a console exactly once, proving the session scope successfully cached the heavy setup. - Test Case 2 (Successful Mocking):
- Action: Execute
test_fetch_user_data_success. - Expected Behavior: The application make a run by to call a URL like
"http://fake-api.com/users". The monkeypatched function intercepts it, returns a fake200status with{"user": "Alice", "age": 28}, and a test safely asserts that data was unpacked correctly without internet access. - Test Case 3 (Graceful Failure Handling):
- Action: Execute
test_fetch_user_data_failure. - Expected Behavior: The monkeypatch simulates a broken API server by returning a fake
500status code. The application catches this aborts the JSON parsing to prevent aJSONDecodeError. Gracefully returnsNonepassing the test assertions.