'RecursionError when mocking function by FastAPI-Testclient
I would like to test the interplay with two FastAPIs using python 3.8. In the running code, one API (main_app) is calling the other API (helper_app) by the function connect_to_helper_app. To test this without setting up two servers, I would like to use fastapi.testclient.TestClient. Unfortunately, I get a RecursionError.
To reproduce the error, you need the following files:
# Content of minimal_example/apps.py
from fastapi import FastAPI
main_app = FastAPI()
helper_app = FastAPI()
def connect_to_helper_app():
"""
This function will be mocked in the test. In the real code, a request to the other app would be made
"""
raise NotImplemented
@main_app.get("/call_helper")
def call_helper() -> dict:
return connect_to_helper_app()
@helper_app.get("/")
def root() -> dict:
return {"msg": "This is the helper app."}
And following test file:
# Content of minimal_example/test.py
from fastapi.testclient import TestClient
from minimal_example.apps import main_app, helper_app
helper_test_client = TestClient(helper_app)
main_test_client = TestClient(main_app)
def test(mocker):
def connect_to_helper_app_with_test_client():
result = helper_test_client.get('/')
return result
mocker.patch('minimal_example.apps.connect_to_helper_app', new=connect_to_helper_app_with_test_client)
main_test_client.get('/call_helper')
The error message I get is:
test.py:7 (test)
mocker = <pytest_mock.plugin.MockerFixture object at 0x0000015DFFB93F40>
def test(mocker):
def connect_to_helper_app_with_test_client():
result = helper_test_client.get('/')
return result
mocker.patch('minimal_example.apps.connect_to_helper_app', new=connect_to_helper_app_with_test_client)
> main_test_client.get('/call_helper')
test.py:14:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\..\.venv\lib\site-packages\requests\sessions.py:542: in get
return self.request('GET', url, **kwargs)
..\..\.venv\lib\site-packages\starlette\testclient.py:415: in request
return super().request(
..\..\.venv\lib\site-packages\requests\sessions.py:529: in request
resp = self.send(prep, **send_kwargs)
..\..\.venv\lib\site-packages\requests\sessions.py:645: in send
r = adapter.send(request, **kwargs)
..\..\.venv\lib\site-packages\starlette\testclient.py:243: in send
raise exc from None
..\..\.venv\lib\site-packages\starlette\testclient.py:240: in send
loop.run_until_complete(self.app(scope, receive, send))
C:\Users\d91802\AppData\Local\Programs\Python\Python38\lib\asyncio\base_events.py:616: in run_until_complete
return future.result()
..\..\.venv\lib\site-packages\fastapi\applications.py:208: in __call__
await super().__call__(scope, receive, send)
..\..\.venv\lib\site-packages\starlette\applications.py:112: in __call__
await self.middleware_stack(scope, receive, send)
..\..\.venv\lib\site-packages\starlette\middleware\errors.py:181: in __call__
raise exc from None
..\..\.venv\lib\site-packages\starlette\middleware\errors.py:159: in __call__
await self.app(scope, receive, _send)
..\..\.venv\lib\site-packages\starlette\exceptions.py:82: in __call__
raise exc from None
..\..\.venv\lib\site-packages\starlette\exceptions.py:71: in __call__
await self.app(scope, receive, sender)
..\..\.venv\lib\site-packages\starlette\routing.py:580: in __call__
await route.handle(scope, receive, send)
..\..\.venv\lib\site-packages\starlette\routing.py:241: in handle
await self.app(scope, receive, send)
..\..\.venv\lib\site-packages\starlette\routing.py:52: in app
response = await func(request)
..\..\.venv\lib\site-packages\fastapi\routing.py:234: in app
response_data = await serialize_response(
..\..\.venv\lib\site-packages\fastapi\routing.py:148: in serialize_response
return jsonable_encoder(response_content)
..\..\.venv\lib\site-packages\fastapi\encoders.py:145: in jsonable_encoder
return jsonable_encoder(
..\..\.venv\lib\site-packages\fastapi\encoders.py:93: in jsonable_encoder
encoded_value = jsonable_encoder(
..\..\.venv\lib\site-packages\fastapi\encoders.py:145: in jsonable_encoder
return jsonable_encoder(
..\..\.venv\lib\site-packages\fastapi\encoders.py:93: in jsonable_encoder
encoded_value = jsonable_encoder(
..\..\.venv\lib\site-packages\fastapi\encoders.py:145: in jsonable_encoder
return jsonable_encoder(
..\..\.venv\lib\site-packages\fastapi\encoders.py:93: in jsonable_encoder
encoded_value = jsonable_encoder(
..\..\.venv\lib\site-packages\fastapi\encoders.py:145: in jsonable_encoder
return jsonable_encoder(
..\..\.venv\lib\site-packages\fastapi\encoders.py:93: in jsonable_encoder
encoded_value = jsonable_encoder(
..\..\.venv\lib\site-packages\fastapi\encoders.py:107: in jsonable_encoder
jsonable_encoder(
..\..\.venv\lib\site-packages\fastapi\encoders.py:145: in jsonable_encoder
return jsonable_encoder(
..\..\.venv\lib\site-packages\fastapi\encoders.py:93: in jsonable_encoder
encoded_value = jsonable_encoder(
..\..\.venv\lib\site-packages\fastapi\encoders.py:145: in jsonable_encoder
return jsonable_encoder(
..\..\.venv\lib\site-packages\fastapi\encoders.py:93: in jsonable_encoder
encoded_value = jsonable_encoder(
E RecursionError: maximum recursion depth exceeded in comparison
!!! Recursion detected (same locals & position)
In the debugger, I see that the function is sucessfully mocked by connect_to_helper_app_with_test_client and within that mocked function, the call to the helper_app returns the expected value ({"msg": "This is the helper app."}).
I would like to understand where the recursion is coming from, and how to avoid the error. Thanks in advance!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
