I want to mock app.requests.Session.get
from test_app.py
to return a mocked requests.Response
object with a 404 status_code
to generate an InvalidPlayerIdException
.
However, no exception is raised as seen from the below output. Is it because I'm using with
clauses, or why doesn't it work?
Reference: https://www.pythontutorial.net/python-unit-testing/python-mock-requests/
Output
:
(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)
You're on the right track, the context manager does interfere with your tests, since your code is calling the __enter__
dunder methods, only once, but twice!
It helps printing your mocks to debug it if you get stuck in a mocking hell.
This is what I got when I printed it:
<MagicMock name='requests.Session().__enter__().get().__enter__()' id='4357377616'>
I then rewrote your test as:
@patch('app.requests')
def test_pokemon_player_requestor_raise_exception(self, mock_requests):
mock_session = MagicMock()
mock_requests.Session = mock_session
session_instance = MagicMock()
mock_session().__enter__.return_value = session_instance
mock_response = MagicMock()
mock_response.status_code = 404
session_instance.get().__enter__.return_value = mock_response
requestor = PokemonPlayerRequestor('https://pokeapi.co')
self.assertRaises(InvalidPlayerIdException, requestor.getPlayerById, 1)
➜ stackoverflow python -m unittest
<MagicMock name='requests.Session().__enter__()' id='4357333536'>
<MagicMock name='requests.Session().__enter__().get().__enter__()' id='4357377616'>
.
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
Hope that helps you.