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

如何用unittest mock requests.Session.get?Mock未触发异常排查

问题:Mock requests.Session.get 未触发预期异常

我希望在test_app.py中mock app.requests.Session.get,使其返回一个status_code为404的模拟requests.Response对象,以此触发InvalidPlayerIdException。但从测试输出可见,预期的异常并未抛出。请问是否是因为使用了with语句导致的,或者是什么其他原因?

测试失败输出

(supersoccer-showdown) ➜  supersoccer-showdown-copy git:(main) ✗ python -m unittest                                                                                               git:(main|…3 
F
======================================================================
FAIL: test_pokemon_player_requestor_raise_exception (test_app.TestPlayerRequestor.test_pokemon_player_requestor_raise_exception)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/opt/homebrew/Cellar/python@3.11/3.11.2_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/unittest/mock.py", line 1369, in patched
    return func(*newargs, **newkeywargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dkNiLyIv/supersoccer-showdown-copy/test_app.py", line 21, in test_pokemon_player_requestor_raise_exception
    self.assertRaises(InvalidPlayerIdException, requestor.getPlayerById, 1)
AssertionError: InvalidPlayerIdException not raised by getPlayerById

app.py 代码

from __future__ import annotations
import abc
import requests

class InvalidPlayerIdException(Exception):
  pass

class Player(abc.ABC):
  def __init__(self, id: int, name: str, weight: float, height: float) -> None:
    self.id = id
    self.name = name
    self.weight = weight
    self.height = height

class PokemonPlayer(Player):
  def __init__(self, id: int, name: str, weight: float, height: float) -> None:
    super().__init__(id, name, weight, height)    

  def __repr__(self) -> str:
    return f'Pokemon(id={self.id},name={self.name},weight={self.weight},height={self.height})'

class PlayerRequestor(abc.ABC):
  def __init__(self, url: str) -> None:
    self.url = url

  @abc.abstractmethod
  def getPlayerCount(self) -> int:
    pass

  @abc.abstractmethod
  def getPlayerById(self, id: int) -> Player:
    pass

class PokemonPlayerRequestor(PlayerRequestor):
  def __init__(self, url: str) -> None:
    super().__init__(url)

  def getPlayerCount(self) -> int:
    with requests.Session() as rs:
      rs.mount('https://', requests.adapters.HTTPAdapter(
        max_retries=requests.urllib3.Retry(total=5, connect=5, read=5, backoff_factor=1)))
      with rs.get(f'{self.url}/api/v2/pokemon/', verify=True) as r:
        r.raise_for_status()
        json = r.json()
        return json["count"]

  def getPlayerById(self, id: int) -> Player:
    with requests.Session() as rs:
      rs.mount('https://', requests.adapters.HTTPAdapter(
        max_retries=requests.urllib3.Retry(total=5, connect=5, read=5, backoff_factor=1)))
      with rs.get(f'{self.url}/api/v2/pokemon/{id}', verify=True) as r:
        if r.status_code == 404:
          raise InvalidPlayerIdException
        r.raise_for_status()
        json = r.json()
        player = PokemonPlayer(id, json["name"], json["weight"], json["height"])
        return player

test_app.py 代码

import unittest
from unittest.mock import MagicMock, patch
from app import *

class TestPlayerRequestor(unittest.TestCase):
  def setUp(self):
    pass

  def tearDown(self):
    pass

  @patch('app.requests')
  def test_pokemon_player_requestor_raise_exception(self, mock_requests):
    mock_response = MagicMock()
    mock_response.status_code = 404
    mock_session = MagicMock()
    mock_requests.Session = mock_session
    instance = mock_session.return_value
    instance.get.return_value = mock_response
    requestor = PokemonPlayerRequestor('https://pokeapi.co')
    self.assertRaises(InvalidPlayerIdException, requestor.getPlayerById, 1)

问题原因与修复方案

  • 核心原因:代码中使用了with rs.get(...) as r的上下文管理器语法,这要求模拟的Response对象必须实现__enter__方法并返回自身。当前的mock_response未配置该方法,导致进入with块后拿到的对象并非你设置了status_code=404的实例,自然不会触发异常判断逻辑。

  • 修复方法:给mock_response添加__enter__方法的模拟,让它返回自身:

    mock_response.__enter__.return_value = mock_response
    
  • 修复后的测试代码:

    @patch('app.requests')
    def test_pokemon_player_requestor_raise_exception(self, mock_requests):
      mock_response = MagicMock()
      mock_response.status_code = 404
      # 添加上下文方法模拟
      mock_response.__enter__.return_value = mock_response
      mock_session = MagicMock()
      mock_requests.Session = mock_session
      instance = mock_session.return_value
      instance.get.return_value = mock_response
      requestor = PokemonPlayerRequestor('https://pokeapi.co')
      self.assertRaises(InvalidPlayerIdException, requestor.getPlayerById, 1)
    

修改后,with语句就能正确拿到设置了404状态码的模拟对象,从而触发InvalidPlayerIdException,测试即可通过。

内容的提问来源于stack exchange,提问作者Shuzheng

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 21:34:59