I am writing a FastAPI application which provides a REST interface for some software system.
I have written two implementations of this software system using a Strategy Pattern.
To illustrate by way of example, here is a MWE code for an example of a similar, but simplified Strategy Pattern. In this example, I have used the concept of a database wrapper class.
The DatabaseWrapperReal
class is incomplete, and in reality would use something like sqlalchemy to talk to a real database.
The DatabaseWrapperFake
class simply provides a mock get
function which returns some predetermined data for testing purposes.
# Software System (library) Code
class DatabaseWrapperReal():
def __init__(self) -> None:
pass
def get(self, key: str) -> str:
# pseudo-implementation. the real implementation of this would
# connect to a database and query it
return f'value'
class DatabaseWrapperFake():
def __init__(self) -> None:
self._data = {
'key1': 'value1',
'key2': 'value2',
'key3': 'value3',
}
def get(self, key: str) -> str:
return self._data[key]
class DatabaseWrapper():
def __init__(self, test_mode: bool = False) -> None:
if test_mode:
self._database_wrapper = DatabaseWrapperFake()
else:
self._database_wrapper = DatabaseWrapperReal()
def get(self, key: str) -> str:
return self._database_wrapper.get(key)
The following code shows how I use a DatabaseWrapper
in the FastAPI code. I initialize it using test_mode=False
. This is fine for normal use, but test_mode
should be set to True
for testing purposes.
# FastAPI code
from fastapi import FastAPI
from pydantic import BaseModel
from database_wrapper import DatabaseWrapper
test_mode = False
database_wrapper = DatabaseWrapper(test_mode=test_mode)
app = FastAPI()
class Key(BaseModel):
key: str
@app.post('/get_by_key')
def get_by_key_endpoint(key: Key):
key = key.key
value = database_wrapper.get(key=key)
return {
'value': value,
}
Finally, here is an example of some test code.
# FastAPI Tests Code
from fastapi.testclient import TestClient
from fastapi_webserver_database_wrapper import app
client = TestClient(app)
def test_fastapi_webserver_database_wrapper():
data = {
'key': 'key1',
}
response = client.post('/get_by_key', json=data)
print(response.json())
assert response.status_code == 200
assert response.json() == {
'value', 'value1',
}
Currently, the test does not pass. It fails, because the test_mode
variable is still set to False
when the test is run. This means that instead of using the DatabaseWrapperFake
class, the DatabaseWrapper
Strategy uses the real implementation, `DatabaseWrapperReal.
Am I structuring my code in the correct way?
- Should I initialize the
database_wrapper
as a global variable from within the FastAPI code? - Should I use a parameter in the constructor of the
DatabaseWrapper
class to switch between the two Strategies? - Assuming the answer to the above questions is “yes”, how should I switch to the Fake implementation Strategy in the test code? (If the above answers are “no” this question is no longer relevant.)