So, I have a server running FastAPI which will make a API call to a remote API upon request.
I am developping unit-testing for this application, but here comes the question:
Can I, for the purpose of the test, replace a legit remote API server response by a predefined response ?
Example of the tests runned:
from fastapi.testclient import TestClient
from web_api import app
client = TestClient(app)
def test_get_root():
response = client.get('/')
assert response.status_code == 200
assert response.json() == {"running": True}
And the my server
from fastapi import FastAPI
app = FastAPI()
#app.get("/")
def home():
return {"running": True}
This is a simple example, but on other endpoints of my API I would call an external remote API
def call_api(self, endpoint:str, params:dict):
url = self.BASEURL + urllib.parse.quote(endpoint)
try:
response = requests.get(url, params=params)
response.raise_for_status()
except requests.exceptions.HTTPError as error:
print(error)
return response
Because I want to test the response of MY API, I would like to replace the remote API with a predefined response.
Also, one user request can end-up in multiple background API requests with transformed pieces of data.
Edit
Here are some more details on the structure of the application:
#app.get("/stuff/.......",
# lots of params
)
def get_stuff_from_things(stuff:list, params):
api = API(api_key=...)
# Do some stuff with the params
things = generate_things_list(params)
api.search_things(params)
# Check the result
# do some other stuff
return some_response
class API:
BASE_URL = 'https://api.example.com/'
def search_things(self, params):
# Do some stuff
# like putting stuff in the params
for s in stuff:
s.update(self.get_thing(params)) # -> get_thing()
# Do some more stuff
return stuff
# get_thing <- search_things
def get_thing(self, params...):
# Some stuff
results = self.call_api('something', params) # -> call_api()
json = results.json()
# Some more stuff
things = []
for thing in json['things']:
t = Thing(thing)
things.append(t)
return things
# call_api <- get_thing
def call_api(self, endpoint:str, params:dict):
url = self.BASEURL + urllib.parse.quote(endpoint)
try:
response = requests.get(url, params=params)
response.raise_for_status()
except requests.exceptions.HTTPError as error:
print(error)
self.last_response = response
return response
Nb. That is pseudo-code, I simplified the functions by removing the parameters, etc.
I hope it is clear, thanks for your help.
A complex API method might look like this (please pay attention to the depends mechanism - it is crucial):
import urllib
import requests
from fastapi import FastAPI, Depends
app = FastAPI()
# this can be in a different file
class RemoteCallWrapper:
def call_api(self, baseurl: str, endpoint: str, params: dict):
url = baseurl + urllib.parse.quote(endpoint)
try:
response = requests.get(url, params=params)
response.raise_for_status()
except requests.exceptions.HTTPError as error:
print(error)
return response
#app.get("/complex_api")
def calls_other_api(remote_call_wrapper=Depends(RemoteCallWrapper)):
response = remote_call_wrapper.call_api("https://jsonplaceholder.typicode.com",
"/todos/1", None)
return {"result": response.json()}
Now, we wish to replace the remote call class. I wrote a helper library that simplifies the replacement for tests - pytest-fastapi-deps:
from fastapi.testclient import TestClient
from mock.mock import Mock
from requests import Response
from web_api import app, RemoteCallWrapper
client = TestClient(app)
class MyRemoteCallWrapper:
def call_api(self, baseurl: str, endpoint: str, params: dict):
the_response = Mock(spec=Response)
the_response.json.return_value = {"my": "response"}
return the_response
def test_get_root(fastapi_dep):
with fastapi_dep(app).override({RemoteCallWrapper: MyRemoteCallWrapper}):
response = client.get('/complex_api')
assert response.status_code == 200
assert response.json() == {"result": {"my": "response"}}
You override the RemoteCallWrapper with your MyRemoteCallWrapper implementation for the test, which has the same spec.
As asserted - the response changed to our predefined response.
It sounds like you'd want to mock your call_api() function.
With a small modification to call_api() (returning the result of .json()), you can easily mock the whole function while calling the endpoint in your tests.
I'll use two files, app.py and test_app.py, to demonstrate how I would do this:
# app.py
import requests
import urllib
from fastapi import FastAPI
app = FastAPI()
def call_api(self, endpoint: str, params: dict):
url = self.BASEURL + urllib.parse.quote(endpoint)
try:
response = requests.get(url, params=params)
response.raise_for_status()
except requests.exceptions.HTTPError as error:
print(error)
return response.json() # <-- This is the only change. Makes it easier to test things.
#app.get("/")
def home():
return {"running": True}
#app.get("/call-api")
def make_call_to_external_api():
# `endpoint` and `params` could be anything here and could be different
# depending on the query parameters when calling this endpoint.
response = call_api(endpoint="something", params={})
# Do something with the response...
result = response["some_parameter"]
return result
# test_app.py
from unittest import mock
from fastapi import status
from fastapi.testclient import TestClient
import app as app_module
from app import app
def test_call_api_endpoint():
test_response = {
"some_parameter": "some_value",
"another_parameter": "another_value",
}
# The line below will "replace" the result of `call_api()` with whatever
# is given in `return_value`. The original function is never executed.
with mock.patch.object(app_module, "call_api", return_value=test_response) as mock_call:
with TestClient(app) as client:
res = client.get("/call-api")
assert res.status_code == status.HTTP_200_OK
assert res.json() == "some_value"
# Make sure the function has been called with the right parameters.
# This could be dynamic based on how the endpoint has been called.
mock_call.assert_called_once_with(endpoint="something", params={})
If app.py and test_app.py are in the same directory you can run the tests simply by running pytest inside that directory.
Related
I need to write test case for a function which use to fetch data from API. In there i used httpx.AsyncClient() as context manager. But i don't understand how to write test case for that function.
async def make_dropbox_request(url, payload, dropbox_token):
async with httpx.AsyncClient(timeout=None, follow_redirects=True) as client:
headers = {
'Content-Type': 'application/json',
'authorization': 'Bearer '+ dropbox_token
}
# make the api call
response = await client.post(url, headers=headers, json=payload)
if response.status_code not in [200]:
print('Dropbox Status Code: ' + str(response.status_code))
if response.status_code in [200, 202, 303]:
return json.loads(response.text)
elif response.status_code == 401:
raise DropboxAuthenticationError()
elif response.status_code == 429:
sleep_time = int(response.headers['Retry-After'])
if sleep_time < 1*60:
await asyncio.sleep(sleep_time)
raise DropboxMaxRateLimitError()
raise DropboxMaxDailyRateLimitError()
raise DropboxHTTPError()
I need to write test cases without calling the API. So there for i believe in this case i need to mock client.post() but i do not understand how to do that. If anyone can help me to figure this out that would be really helpful for me.
This image also include my code block
TL;DR: use return_value.__aenter__.return_value to mock the async context.
Assuming you are using Pytest and pytest-mock, your can use the mocker fixture to mock httpx.AsyncClient.
Since the post function is async, you will need to use an AsyncMock.
Finally, since you use an async context, you will also need to use return_value.__aenter__.return_value to properly mock the returned context. Note for a synchronous context, simply use __enter__ instead of __aenter__.
#pytest.fixture
def mock_AsyncClient(mocker: MockerFixture) -> Mock:
mocked_AsyncClient = mocker.patch(f"{TESTED_MODULE}.AsyncClient")
mocked_async_client = Mock()
response = Response(status_code=200)
mocked_async_client.post = AsyncMock(return_value=response)
mocked_AsyncClient.return_value.__aenter__.return_value = mocked_async_client
return mocked_async_client
I also faced with same issue and handled it with patch decorator. I share my code, so that might help for others.
from unittest.mock import patch
import pytest
import httpx
from app.services import your_service
#pytest.mark.anyio
#patch(
'app.services.your_service.httpx.AsyncClient.post',
return_value = httpx.Response(200, json={'id': '9ed7dasdasd-08ff-4ae1-8952-37e3a323eb08'})
)
async def test_get_id(mocker):
result = await your_service.get_id()
assert result == '9ed7dasdasd-08ff-4ae1-8952-37e3a323eb08'
You can try out the RESPX mocking library to test and mock your HTTPX clients.
In your case, something like this should do it:
async def make_dropbox_request(url, payload, dropbox_token):
...
response = await client.post(url, headers=headers, json=payload)
...
return response.json()
#respx.mock
async def test_dropbox_endpoint():
url = "https://dropbox-api/some-endpoint/"
endpoint = respx.post(url).respond(json={"some": "data"})
result = await make_dropbox_request(url, ..., ...)
assert endpoint.called
assert result == {"some": "data"}
To be dry and not repeat the mocking in each test, you can set up your own pytest fixture, or respx instance, globally that pre-mocks all dropbox api endpoints, and then in each test just alter response/error depending on the scenario for the test, to get full test coverage on make_dropbox_request.
#pytest.fixture()
async def dropbox_mock():
async with respx.mock() as dropbox:
# default endpoints and their responses
dropbox.post("some-endpoint", name="foo").respond(404)
dropbox.post("some-other-endpoint", name="bar").respond(404)
# ^ name routes for access in tests
yield dropbox
async def test_some_case(dropbox_mock):
dropbox_mock["foo"].respond(json={})
....
I built an API with FastAPI that interacts with DynamoDB.
In the beginning of my journey in Test Driven Development, I have doubts about what to mock.
This is the get method, main.py:
router = FastAPI()
#router.get("/{device_id}")
def get_data(request: Request, device_id: str, query: DataQuery = Depends(DataQuery.depends)):
da_service = DaService()
try:
start_time, end_time = DaService.validate_dates(query.start, query.end)
return 'OK'
except WrongDataFormat as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail='Internal Server Error')
In the test file I started by creating the success test, test_main.py:
from fastapi.testclient import TestClient
from unittest import mock
from utils.exceptions import WrongDataFormat
from endpoints.datalake import router
client = TestClient(router)
def test_success_response():
with mock.patch('endpoints.datalake.DataApiService.get_datalake_data'):
response = client.get('/xxxxx', params = {'start': '1629886483', 'end': '1629886504'})
assert response.status_code == 200
assert isinstance(response.json(), dict)
Now I want to create the test for when the exception WrongDataFormat is returned, but I'm not succeeding... This is what I have right now:
def test_exception_response_():
response = client.get('/xxxxx', params = {'2021-08-28', 'end': '2021-12-25'})
assert response.status_code == 400
How can I mock the function main.validate_dates to return the exception WrongDataFormat and assert it correctly?
If you want to test the status code and message of a response you have to use TestClient(app) where app is the FastAPI application. Converting the exception into the appropriate response is the task of the application, not the router (which is what you're testing with).
client = TestClient(app)
This way you can test the API of your application (which is the most useful surface to test, imho).
I'm trying to check if a specific model was used as an input parser for a FastAPI route. However, I'm not sure how to patch (or spy on) it.
I have the following file structure:
.
└── roo
├── __init__.py
├── main.py
└── test_demo.py
main.py:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class ItemModel(BaseModel):
name: str
#app.post("/")
async def read_main(item: ItemModel):
return {"msg": f"Item: {item.name}"}
test_demo.py:
from fastapi.testclient import TestClient
from unittest.mock import patch
from roo.main import app, ItemModel
client = TestClient(app)
def test_can_creating_new_item_users_proper_validation_model():
with patch('roo.main.ItemModel', wraps=ItemModel) as patched_model:
response = client.post("/", json={'name': 'good'})
assert response.status_code == 200
assert response.json() == {"msg": "Item: good"}
assert patched_model.called
However, patched_model is never called (other asserts pass). I don't want to change the functionality or replace ItemModel in main.py, I just want to check if it was used.
My first approach to this was to wrap the read_main method and check that the item passed into the function is indeed an instance of ItemModel. But that was a dead-end approach because of the way FastAPI endpoints are prepared and stored: FastAPI stores a copy of the endpoint function objects in a list: (see fastapi/routing.py), then evaluates at request-time which endpoint to call.
from roo.main import app
def test_read_main():
assert 'read_main' in [r.endpoint.__name__ for r in app.routes]
# check that read_main was called *and* received an ItemModel instance?
My second approach involves spying or "breaking" the initialization of ItemModel, such that if the endpoint does indeed use that model, then a "broken" ItemModel would cause a request that hits that endpoint to fail. We "break" ItemModel by making use of the fact that (1) FastAPI calls the __init__ of your model during the request-response cycle, and (2) a 422 error response is propagated by default when the endpoint is unable to serialize a model properly:
class ItemModel(BaseModel):
name: str
def __init__(__pydantic_self__, **data: Any) -> None:
print("Make a POST request and confirm that this is printed out")
super().__init__(**data)
So in tests, just mock the __init__ method:
Example for pytest
import pytest
from fastapi.testclient import TestClient
from roo.main import app, ItemModel
def test_read_main(monkeypatch: pytest.MonkeyPatch):
client = TestClient(app)
def broken_init(self, **data):
pass # `name` and other fields won't be set
monkeypatch.setattr(ItemModel, '__init__', broken_init)
with pytest.raises(AttributeError) as exc:
client.post("/", json={'name': 'good'})
assert 422 == response.status_code
assert "'ItemModel' object has no attribute" in str(exc.value)
Example for pytest + pytest-mock's mocker.spy
from fastapi.testclient import TestClient
from pytest_mock import MockerFixture
from roo.main import app, ItemModel
def test_read_main(mocker: MockerFixture):
client = TestClient(app)
spy = mocker.spy(ItemModel, '__init__')
client.post("/", json={'name': 'good'})
spy.assert_called()
spy.assert_called_with(**{'name': 'good'})
Example for unittest
from fastapi.testclient import TestClient
from roo.main import app, ItemModel
from unittest.mock import patch
def test_read_main():
client = TestClient(app)
# Wrapping __init__ like this isn't really correct, but serves the purpose
with patch.object(ItemModel, '__init__', wraps=ItemModel.__init__) as mocked_init:
response = client.post("/", json={'name': 'good'})
assert 422 == response.status_code
mocked_init.assert_called()
mocked_init.assert_called_with(**{'name': 'good'})
Again, the tests check that the endpoint fails in either serializing into an ItemModel or in accessing item.name, which will only happen if the endpoint is indeed using ItemModel.
If you modify the endpoint from item: ItemModel into item: OtherModel:
class OtherModel(BaseModel):
name: str
class ItemModel(BaseModel):
name: str
#app.post("/")
async def read_main(item: OtherModel): # <----
return {"msg": f"Item: {item.name}"}
then running the tests should now fail because the endpoint is now creating the wrong object:
def test_read_main(mocker: MockerFixture):
client = TestClient(app)
spy = mocker.spy(ItemModel, '__init__')
client.post("/", json={'name': 'good'})
> spy.assert_called()
E AssertionError: Expected '__init__' to have been called.
test_demo_spy.py:11: AssertionError
with pytest.raises(AttributeError) as exc:
response = client.post("/", json={'name': 'good'})
> assert 422 == response.status_code
E assert 422 == 200
E +422
E -200
test_demo_pytest.py:15: AssertionError
The assertion errors for 422 == 200 is a bit confusing, but it basically means that even though we "broke" ItemModel, we still got a 200/OK response.. which means ItemModel is not being used.
Likewise, if you modified the tests first and mocked-out the __init__ of OtherModel instead of ItemModel, then running the tests without modifying the endpoint will result in similar failing tests:
def test_read_main(mocker: MockerFixture):
client = TestClient(app)
spy = mocker.spy(OtherModel, '__init__')
client.post("/", json={'name': 'good'})
> spy.assert_called()
E AssertionError: Expected '__init__' to have been called.
def test_read_main():
client = TestClient(app)
with patch.object(OtherModel, '__init__', wraps=OtherModel.__init__) as mocked_init:
response = client.post("/", json={'name': 'good'})
# assert 422 == response.status_code
> mocked_init.assert_called()
E AssertionError: Expected '__init__' to have been called.
The assertion here is less confusing because it says we expected that the endpoint will call OtherModel's __init__, but it wasn't called. It should pass after modifying the endpoint to use item: OtherModel.
One last thing to note is that since we are manipulating the __init__, then it can cause the "happy path" to fail, so it should now be tested separately. Make sure to undo/revert the mocks and patches:
Example for pytest
def test_read_main(monkeypatch: pytest.MonkeyPatch):
client = TestClient(app)
def broken_init(self, **data):
pass
# Are we really using ItemModel?
monkeypatch.setattr(ItemModel, '__init__', broken_init)
with pytest.raises(AttributeError) as exc:
response = client.post("/", json={'name': 'good'})
assert 422 == response.status_code
assert "'ItemModel' object has no attribute" in str(exc.value)
# Okay, really using ItemModel. Does it work correctly?
monkeypatch.undo()
response = client.post("/", json={'name': 'good'})
assert response.status_code == 200
assert response.json() == {"msg": "Item: good"}
Example for pytest + pytest-mock's mocker.spy
from pytest_mock import MockerFixture
from fastapi.testclient import TestClient
from roo.main import app, ItemModel
def test_read_main(mocker: MockerFixture):
client = TestClient(app)
# Are we really using ItemModel?
spy = mocker.spy(ItemModel, '__init__')
client.post("/", json={'name': 'good'})
spy.assert_called()
spy.assert_called_with(**{'name': 'good'})
# Okay, really using ItemModel. Does it work correctly?
mocker.stopall()
response = client.post("/", json={'name': 'good'})
assert response.status_code == 200
assert response.json() == {"msg": "Item: good"}
Example for unittest
def test_read_main():
client = TestClient(app)
# Are we really using ItemModel?
with patch.object(ItemModel, '__init__', wraps=ItemModel.__init__) as mocked_init:
response = client.post("/", json={'name': 'good'})
assert 422 == response.status_code
mocked_init.assert_called()
mocked_init.assert_called_with(**{'name': 'good'})
# Okay, really using ItemModel. Does it work correctly?
response = client.post("/", json={'name': 'good'})
assert response.status_code == 200
assert response.json() == {"msg": "Item: good"}
All in all, you might want to consider if/why it's useful to check for which model is exactly used. Normally, I just check that passing-in valid request params returns the expected valid response, and likewise, that invalid requests returns an error response.
I wrote an endpoint that calls an API to get the time of day for a timezone defined by the user. Now I need to Mock this endpoint but I am having trouble coming up with the correct answer. Here is the code that I wrote:
Im not quite sure what I am suppose to call to get a response.
import requests
import json
import jsonpath
import dateutil
from flask import Flask, render_template, request
from flask import jsonify, make_response
app = Flask(__name__, template_folder="templates")
#app.route('/get_time', methods=['GET'])
def get_time():
try:
time_zone = request.args.get('time_zone')
url = "http://worldclockapi.com/api/json/" + time_zone + "/now"
r = requests.get(url)
except Exception:
return make_response(jsonify({"Error": "Some error message"}), 400)
return r.json()["currentDateTime"]
if response.status_code != 200:
print("Error on response")
return response.status_code, response.text
if __name__ == '__main__':
app.run(debug=True)
This is what I have for the Test:
import json
import unittest
import unittest.mock
import requests
#name of the file being tested
import timeofday
class MockResponse:
def __init__(self, text, status_code):
self.text = text
self.status_code = status_code
def json(self):
return json.loads(self.text)
def __iter__(self):
return self
def __next__(self):
return self
#json returned by the API http://worldclockapi.com/api/json/est/now
def mock_requests_timeofday(*args, **kwargs):
text = """
{
"$id": "1",
"currentDateTime": "2019-11-08T15:52-05:00",
"utcOffset": "-05:00:00",
"isDayLightSavingsTime": false,
"dayOfTheWeek": "Friday",
"timeZoneName": "Eastern Standard Time",
"currentFileTime": 132177019635463680,
"ordinalDate": "2019-312",
"serviceResponse": null
}}
"""
response = MockResponse(text, 200)
return response
class TestLocation(unittest.TestCase):
#unittest.mock.patch('requests.get', mock_requests_get_success)
def test_get_time(self):
self.assertEqual(response.status_code, 200)
class TestTimeofday(unittest.TestCase):
#unittest.mock.patch('timeofday.requests.get', mock_requests_timeofday)
def get_time(self):
self.assertEqual(response.status_code, 200)
Your code is currently failing because you directly mock the get function of the module you've imported in your test file.
In order to make your test work, you will have to mock directly the requests.get method of your other file.
This is what a mock of the get method you did in the timeofday.py could look like:
mock.patch('timeofday.requests.get', mock_requests_get_success)
Now when you execute get_time the API call should be mocked and you will receive the answer you defined.
PS: Be aware that the if statement you wrote after the return r.json()["currentDateTime"] will never be executed because your function is ending when you use return.
I have created a simple flask app that and I'm reading the response from python as:
response = requests.post(url,data=json.dumps(data), headers=headers )
data = json.loads(response.text)
Now my issue is that under certain conditions I want to return a 400 or 500 message response. So far I'm doing it like this:
abort(400, 'Record not found')
#or
abort(500, 'Some error...')
This does print the message on the terminal:
But in the API response I kept getting a 500 error response:
The structure of the code is as follows:
|--my_app
|--server.py
|--main.py
|--swagger.yml
Where server.py has this code:
from flask import render_template
import connexion
# Create the application instance
app = connexion.App(__name__, specification_dir="./")
# read the swagger.yml file to configure the endpoints
app.add_api("swagger.yml")
# Create a URL route in our application for "/"
#app.route("/")
def home():
"""
This function just responds to the browser URL
localhost:5000/
:return: the rendered template "home.html"
"""
return render_template("home.html")
if __name__ == "__main__":
app.run(host="0.0.0.0", port="33")
And main.py has all the function I'm using for the API endpoints.
E.G:
def my_funct():
abort(400, 'Record not found')
When my_funct is called, I get the Record not found printed on the terminal, but not in the response from the API itself, where I always get the 500 message error.
You have a variety of options:
The most basic:
#app.route('/')
def index():
return "Record not found", 400
If you want to access the headers, you can grab the response object:
#app.route('/')
def index():
resp = make_response("Record not found", 400)
resp.headers['X-Something'] = 'A value'
return resp
Or you can make it more explicit, and not just return a number, but return a status code object
from flask_api import status
#app.route('/')
def index():
return "Record not found", status.HTTP_400_BAD_REQUEST
Further reading:
You can read more about the first two here: About Responses (Flask quickstart)
And the third here: Status codes (Flask API Guide)
I like to use the flask.Response class:
from flask import Response
#app.route("/")
def index():
return Response(
"The response body goes here",
status=400,
)
flask.abort is a wrapper around werkzeug.exceptions.abort which is really just a helper method to make it easier to raise HTTP exceptions. That's fine in most cases, but for restful APIs, I think it may be better to be explicit with return responses.
Here's some snippets from a Flask app I wrote years ago. It has an example of a 400 response
import werkzeug
from flask import Flask, Response, json
from flask_restplus import reqparse, Api, Resource, abort
from flask_restful import request
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
api = Api(app)
parser = reqparse.RequestParser()
parser.add_argument('address_to_score', type=werkzeug.datastructures.FileStorage, location='files')
class MissingColumnException(Exception):
pass
class InvalidDateFormatException(Exception):
pass
#api.route('/project')
class Project(Resource):
#api.expect(parser)
#api.response(200, 'Success')
#api.response(400, 'Validation Error')
def post(self):
"""
Takes in an excel file of addresses and outputs a JSON with scores and rankings.
"""
try:
df, input_trees, needed_zones = data.parse_incoming_file(request)
except MissingColumnException as e:
abort(400, 'Excel File Missing Mandatory Column(s):', columns=str(e))
except Exception as e:
abort(400, str(e))
project_trees = data.load_needed_trees(needed_zones, settings['directories']['current_tree_folder'])
df = data.multiprocess_query(df, input_trees, project_trees)
df = data.score_locations(df)
df = data.rank_locations(df)
df = data.replace_null(df)
output_file = df.to_dict('index')
resp = Response(json.dumps(output_file), mimetype='application/json')
resp.status_code = 200
return resp
#api.route('/project/health')
class ProjectHealth(Resource):
#api.response(200, 'Success')
def get(self):
"""
Returns the status of the server if it's still running.
"""
resp = Response(json.dumps('OK'), mimetype='application/json')
resp.status_code = 200
return resp
You can return a tuple with the second element being the status (either 400 or 500).
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello():
return "Record not found", 400
if __name__ == '__main__':
app.run()
Example of calling the API from python:
import requests
response = requests.get('http://127.0.0.1:5000/')
response.text
# 'This is a bad request!'
response.status_code
# 400
I think you're using the abort() function correctly. I suspect the issue here is that an error handler is that is catching the 400 error and then erroring out which causes the 500 error. See here for more info on flask error handling.
As an example, the following would change a 400 into a 500 error:
#app.errorhandler(400)
def handle_400_error(e):
raise Exception("Unhandled Exception")
If you're not doing any error handling, it could be coming from the connexion framework, although I'm not familiar with this framework.
You can simply use #app.errorhandler decorator.
example:
#app.errorhandler(400)
def your_function():
return 'your custom text', 400