I'm creating test cases for a FastAPI endpoint that uses motor and a keep getting assert "object MagicMock can't be used in 'await' expression.
Endpoint:
@router.put("/shield")
async def action(a: bool, mongo_client: AsyncIOMotorClient = Depends(connection)):
try:
col = mongo_client["db"]["col"]
result = await col.update_one(
{"foo": "bar"}, {"$set": {"x": a}}
)
if result.matched_count == 0:
return JSONResponse(status_code=404, content="Str not found")
elif result.modified_count == 0:
return JSONResponse(status_code=204, content="Failed to update")
return JSONResponse(status_code=200, content="update successfull")
except Exception as e:
return JSONResponse(status_code=500, content=str(e))
And my test case is like this:
@pytest.fixture(scope="session")
async def mock_motor_client():
mock_client = AsyncMock()
yield mock_client
@pytest.mark.asyncio
async def test_shield_disable_success(mock_motor_client):
app = FastAPI()
app.include_router(router)
app.dependency_overrides[connection] = lambda: mock_motor_client
client = TestClient(app)
response = client.put("/v2/shield?a=false")
mock_motor_client.update_one = AsyncMock(return_value=AsyncMock(matched_count=1, modified_count=1))
assert response.status_code == 200
Here the minimal code to make your example works
from unittest.mock import AsyncMock
import pytest
from fastapi import APIRouter, Depends, FastAPI
from motor.motor_asyncio import AsyncIOMotorClient
from starlette.responses import JSONResponse
from starlette.testclient import TestClient
router = APIRouter(prefix="/v2")
async def connection() -> AsyncIOMotorClient:
return AsyncIOMotorClient()
@router.put("/shield")
async def action(a: bool,
mongo_client: AsyncIOMotorClient = Depends(connection)):
try:
col = mongo_client["db"]["col"]
result = await col.update_one(
{"foo": "bar"}, {"$set": {"x": a}}
)
if result.matched_count == 0:
return JSONResponse(status_code=404, content="Str not found")
elif result.modified_count == 0:
return JSONResponse(status_code=204, content="Failed to update")
return JSONResponse(status_code=200, content="update successfull")
except Exception as e:
return JSONResponse(status_code=500, content=str(e))
@pytest.fixture(scope="session")
def mock_motor_client():
mock_client = AsyncMock()
mock_client.update_one = AsyncMock(return_value=AsyncMock(matched_count=1, modified_count=1))
return {"db": {"col": mock_client}}
@pytest.mark.asyncioasync def test_shield_disable_success(mock_motor_client):
app = FastAPI()
app.include_router(router)
app.dependency_overrides[connection] = lambda: mock_motor_client
client = TestClient(app)
response = client.put("/v2/shield?a=false")
assert response.status_code == 200
The main changes are on the fixture of the mock_motor_client
where, you have to return a dict (not a function) because it's the waited structure in your router function, you have this line: mongo_client["db"]["col"]
(this could be done in the connection
function). And, the second mock giving the value is also done in this fixture not in the test (it's cleaner IHMO).