Mock External APIs in Python Tests
Learn how to mock external API calls in Python tests using unittest.mock, so your tests stay fast, reliable, and independent of network or third-party services.
Here is the article you requested, written by a human for PythonSkillset.com.
Stop Hitting the Real Server: How to Mock External APIs in Python Tests
We have all been there. You write a beautiful function that makes a call to a third-party API like Stripe, Twilio, or the GitHub API. Your code looks perfect. You run your tests... and thunk. The test fails because your internet is slow, the API key is expired, or the service is down for maintenance.
A test that relies on the outside world isn't a test; it's a liability.
At PythonSkillset, we believe that fast, reliable tests are the bedrock of a maintainable project. The solution is simple: mock the external API. Let’s look at how to do this without the usual headache.
Why Mock?
Mocking means you replace a part of your system (like the requests library) with a fake object that behaves exactly how you tell it to. This gives you three superpowers:
- Speed: No network calls. Your test suite runs in milliseconds.
- Control: You can simulate a 500 error, a timeout, or a specific JSON response.
- Cost: You don’t burn through your API call quota every time you run a test.
The "Monkeypatch" Method (Built-in Power)
Python’s standard library comes with unittest.mock. This is the most straightforward way to do it, especially if you are using pytest.
Imagine we have a file called weather.py that gets the current temperature from an external API.
# weather.py
import requests
def get_temperature(city):
url = f"https://api.weatherapi.com/v1/current.json?key=YOUR_KEY&q={city}"
response = requests.get(url)
data = response.json()
return data['current']['temp_c']
To test this, we don't want to actually call weatherapi.com. We want to intercept the requests.get call.
# test_weather.py
from unittest.mock import patch
from weather import get_temperature
def test_get_temperature_success():
# 1. Define what the mock should return
mock_response = {
"current": {
"temp_c": 22.5
}
}
# 2. Patch the requests.get function inside the weather module
with patch('weather.requests.get') as mock_get:
# 3. Configure the mock
mock_get.return_value.json.return_value = mock_response
# 4. Run the actual function
result = get_temperature("London")
# 5. Assertions
assert result == 22.5
mock_get.assert_called_once()
Let's break down the magic inside the with block:
- patch('weather.requests.get') : This tells python, "For the duration of this test, whenever the weather module tries to use requests.get, give it this fake object instead."
- mock_get.return_value.json.return_value = mock_response : This chains the behavior. When our code calls requests.get(), it gets the mock. When it calls .json() on that mock, it returns our dictionary.
Simulating the Bad Days
The real beauty of mocking is that you can test your error handling without hurting anything.
import requests
from unittest.mock import patch
import pytest
def test_get_temperature_timeout():
with patch('weather.requests.get') as mock_get:
# Make the mocked call raise an exception
mock_get.side_effect = requests.exceptions.Timeout
# Expect your function to raise an error or handle it gracefully
# In this case, let's assume our function was updated to return None on error
result = get_temperature("UnknownCity")
assert result is None
Using side_effect allows you to raise exceptions or return different values based on the input, making your tests incredibly robust.
When to Use Different Tools
At PythonSkillset, we often see developers go straight to complex libraries. But the built-in unittest.mock is often the best tool for 80% of cases.
- Use
unittest.mock(with pytest): When you only need to mock a single HTTP call or a specific library function. - Use
responseslibrary: If you are mocking a lot of complex requests or want to match specific request URLs and headers strictly. - Use
VCR.py: If you want to record the real API response once and replay it forever in your tests. This is great for legacy systems where you don't want to define the response manually.
A Simple Rule for PythonSkillset
Keep it simple. If your function calls requests.get, use patch. If you need to test a class that creates a database session, use patch. The testing framework is your friend.
Don't let your tests be hostages to the internet. Mock the outside world, control the data, and sleep soundly knowing your code works—even when the server doesn't.
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.