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.
Dependsto get the relevant API client provided into your relevant locations (i.e. in other services or in the view itself). When using pytest you can use the dependency override support: fastapi.tiangolo.com/advanced/testing-dependencies to provide an alternative client (with pre-made responses for example).common-parameterswhich just act as a common parameter for both endpoint, I don't really see how I can useDependsin my case (Where I get user input, transform it, and then proceed to different API Calls with pieces of data)api_client: ApiClient = Depends(get_api_client)(it's hard to say since you haven't shown anything about how you intend to use the remove API from your views), then overrideget_api_clientin your dependency overview. You can use theDependssystem to compose services this way, for exampledata_service: DataService = Depends(get_data_service)withget_data_service(api_client: ApiClient = Depends(get_api_client):.. etc. That will let you override just parts of the hierarchy in your tests, or larger blocks if necessary (i.e. overrideget_data_serviceinstead.