diff --git a/tests/test_dump_json_fast_path.py b/tests/test_dump_json_fast_path.py new file mode 100644 index 0000000000..d41d5aa66f --- /dev/null +++ b/tests/test_dump_json_fast_path.py @@ -0,0 +1,51 @@ +from unittest.mock import patch + +from fastapi import FastAPI +from fastapi.responses import JSONResponse +from fastapi.testclient import TestClient +from pydantic import BaseModel + + +class Item(BaseModel): + name: str + price: float + + +app = FastAPI() + + +@app.get("/default") +def get_default() -> Item: + return Item(name="widget", price=9.99) + + +@app.get("/explicit", response_class=JSONResponse) +def get_explicit() -> Item: + return Item(name="widget", price=9.99) + + +client = TestClient(app) + + +def test_default_response_class_skips_json_dumps(): + """When no response_class is set, the fast path serializes directly to + JSON bytes via Pydantic's dump_json and never calls json.dumps.""" + with patch( + "starlette.responses.json.dumps", wraps=__import__("json").dumps + ) as mock_dumps: + response = client.get("/default") + assert response.status_code == 200 + assert response.json() == {"name": "widget", "price": 9.99} + mock_dumps.assert_not_called() + + +def test_explicit_response_class_uses_json_dumps(): + """When response_class is explicitly set to JSONResponse, the normal path + is used and json.dumps is called via JSONResponse.render().""" + with patch( + "starlette.responses.json.dumps", wraps=__import__("json").dumps + ) as mock_dumps: + response = client.get("/explicit") + assert response.status_code == 200 + assert response.json() == {"name": "widget", "price": 9.99} + mock_dumps.assert_called_once()