As I am switching from Django to FastAPI, I also need to change tests from unittests to pytests. I build a custom TestAPI class and have test cases as methods, which works fine. However I want to override some functions (not dependencies) which are used in the code in one testcase. I tried this, but it doesn't work:
def test_smoke_me_api(self, monkeypatch):
monkeypatch.setattr("app.auth.utils.get_user", mock_get_user)
re = self.c.get("/me/")
It doesn't call the mock_get_user
function, but instead the get_user
one. According to some docs, I added the monkeypatch to the setup_class
function of my test class, but this didn't work as this is apparently initialized with one argument only (self
).
self.c
is a client, which is a TestClient
initialized in the setup_class
.
Minimal example:
app/auth/utils.py
def get_user(sub) -> dict:
re = requests.get(f"https://{API_DOMAIN}/api/v2/users/{sub}")
return re.json()
app/auth/views.py
from app.auth.utils import get_user
@router.get("/")
async def me_get(sub: str = Security(auth.verify)) -> dict:
return get_user(sub)
app/test_main.py
def mock_get_user(sub = "testing") -> dict:
return {
"created_at": "2023-08-15T13:25:31.507Z",
"email": "test@test.org"
}
class TestAPI:
def setup_class(self):
from app.main import app
self.c = TestClient(app)
def test_smoke_me_api(self, monkeypatch):
monkeypatch.setattr("app.auth.utils.get_user", mock_get_user)
re = self.c.get("/me/")
When in app.auth.views
you run from app.auth.utils import get_user
, you're creating a new reference to the original function get_user
inside the app.auth.views
module.
Patching app.auth.utils.get_user
changes the reference in app.auth.utils
, but it leaves app.auth.views
with the copy it previously made.
There are two general approaches to fixing this:
import
the module, not the function.
That is to say, do something more like import app.auth.utils as auth_utils
, then call auth_utils.get_user()
to refer back through the namespace to which you're applying the monkeypatch
Monkeypatch the destination, not the source
That is to say, instead of monkeypatching app.auth.utils.get_user
, monkeypatch app.auth.views.get_user()
.