Pytest:Mock requests.get模拟API 400错误未抛出RequestException问题
解决pytest测试中API 400错误场景的mock状态泄漏问题
问题原因
你遇到的问题本质是测试状态泄漏,核心原因大概率是get_exchange_rate函数使用了缓存(比如functools.lru_cache):
- 前两个测试调用
get_exchange_rate('USD', 'EUR')后,成功的结果被缓存 - 第三个测试再调用相同参数时,直接返回缓存的结果,根本不会执行到mock的
requests.get逻辑,自然不会触发400错误的RequestException - 单独运行第三个测试时,缓存为空,函数会执行mock的请求逻辑,因此测试通过
另外需要确认get_exchange_rate内部是否正确调用了response.raise_for_status()——这是触发400异常的关键步骤,如果没调用,即使mock返回400状态码也不会抛出异常。
解决方案
1. 自动清除函数缓存
添加一个自动执行的fixture,在每个测试前清除get_exchange_rate的缓存,确保每次调用都重新执行请求逻辑:
@pytest.fixture(autouse=True) def clear_exchange_rate_cache(): get_exchange_rate.cache_clear()
2. 确认get_exchange_rate的实现
确保函数内部调用了response.raise_for_status(),示例如下:
def get_exchange_rate(base, target): # 构造请求URL等逻辑 response = requests.get(api_url, params=...) response.raise_for_status() # 必须调用此方法触发HTTP异常 data = response.json() # 后续处理逻辑
3. 优化测试独立性(可选)
给第三个测试使用不同的参数,避免和前序测试的缓存键冲突,比如测试get_exchange_rate('USD', 'GBP'),但这种方式不如清除缓存通用。
修改后的完整测试代码
import datetime import pytest import requests from src.currency_converter import (convert_currency, get_exchange_rate, humanize_time) mock_response_json = { "rates": { "EUR": 0.85, "USD": 1.0, }, "time_last_updated": int(datetime.datetime.now().timestamp()) } class MockResponse: def __init__(self, mock_response_json, status_code): self.mock_response_json = mock_response_json self.status_code = status_code def raise_for_status(self): if self.status_code >= 400: raise requests.exceptions.RequestException(f"{self.status_code} Error") def json(self): return self.mock_response_json @pytest.fixture(autouse=True) def clear_exchange_rate_cache(): get_exchange_rate.cache_clear() @pytest.fixture def mock_response_success(monkeypatch): def mock_get(*args, **kwargs): mock_obj = MockResponse(mock_response_json, 200) return mock_obj monkeypatch.setattr('requests.get', mock_get) def test_get_exchange_rate_success(mock_response_success): exchange_rate, time_last_updated = get_exchange_rate('USD', 'EUR') assert exchange_rate == 0.85 assert isinstance(time_last_updated, int) def test_get_exchange_rate_currency_not_found(mock_response_success): with pytest.raises(ValueError, match="Currency 'IRR' not found in the API response."): get_exchange_rate('USD', 'IRR') def test_get_exchange_rate_currency_not_response(monkeypatch): def mock_get_reqerror(*args, **kwargs): return MockResponse(mock_response_json, 400) monkeypatch.setattr('requests.get', mock_get_reqerror) with pytest.raises(requests.exceptions.RequestException, match="400 Error"): get_exchange_rate('USD', 'EUR')
内容的提问来源于stack exchange,提问作者s.pakdel
相关产品推荐
相关产品推荐

