I have some existing FastAPI tests which no longer pass, because some internal server logic has changed such that there is now a dependency on a value returned by datetime.now()
.
This is an external dependency. Normally to handle external dependencies, we would write a mock implementation of the external dependency and find a way to inject it into the code being tested.
I am not sure if that is the best approach for something as simple as a datetime dependency. It might be, or it might not be.
Here is some MWE code which illustrates the problem:
# lib_datetime.py
from datetime import datetime
from datetime import timezone
def now() -> datetime:
return datetime.now(timezone.utc)
# fastapi_webserver.py
from fastapi import FastAPI
from lib_datetime import now
from datetime import datetime
app = FastAPI()
@app.get('/datetime_now')
def datetime_now():
the_datetime_now = now()
the_datetime_now_str = the_datetime_now.strftime('%Y-%m-%d %H:%M:%S.%f %z')
return {
'datetime_now': the_datetime_now_str,
}
# test_fastapi_webserver.py
from fastapi.testclient import TestClient
from fastapi_webserver import app
from datetime import datetime
from datetime import timezone
client = TestClient(app)
def test_fastapi_webserver_datetime_now():
datetime_now = datetime(year=2024, month=7, day=20, tzinfo=timezone.utc)
datetime_now_str = datetime_now.strftime('%Y-%m-%d %H:%M:%S.%f %z')
response = client.get('/datetime_now')
print(response.json())
assert response.status_code == 200
assert response.json() == {
'datetime_now', datetime_now_str,
}
The problem should be fairly obvious. The value of the datetime returned varies depending on when the test is run. No good.
Here is a summary of what I have tried to fix it:
- Monkeypatching. I tried to change what
lib_datetime.now()
points to from within the test file. I couldn’t figure out how to make this work. - Hoisting external dependencies. In theory,
now()
could depend on an object. Or it could become an object. We could create two objects, one which returns the current datetime, and one which returns a fixed value for testing. I could not figure out how to inject that into the FastAPIapp
, or even if this is possible. This would be my prefered approach as it is what I am most familiar with. - Use an environment variable to change the runtime behaviour of
now()
. Again, I was not sure how to integrate this into the test code, and I am not sure this is really a good approach.
The blocker here is that I am not that familiar with FastAPI, as I only recently started using it. So I don’t know what I can do with it, yet.