如何用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
相关产品推荐
相关产品推荐

