You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 17:55:54