uma*_*mat 11 python pytest python-unittest starlette fastapi
我有一个简单的FastAPI应用程序,我正在尝试为其创建测试pytest。
我的目标是测试应用程序在出现不同错误时的行为方式。
\n\n我的应用程序中有一个简单的健康检查路线:
\n\nfrom fastapi import APIRouter\n\nrouter = APIRouter()\n\n\n@router.get("/health")\nasync def health():\n return "It\'s working \xe2\x9c\xa8"\nRun Code Online (Sandbox Code Playgroud)\n\n现在在我的 pytest 模块中,我正在尝试修补上述函数,以便它引发不同的错误。\n我正在使用,unittest.mock但我得到了非常奇怪的行为。
import pytest\nfrom unittest import mock\n\nfrom fastapi import HTTPException\nfrom starlette.testclient import TestClient\n\nimport app.api.health\nfrom app.main import app # this is my application (FastAPI instance) with the `router` attached\n\n\n@pytest.fixture()\ndef client():\n with TestClient(app) as test_client:\n yield test_client\n\n\ndef test_simple(client):\n def mock_health_function():\n raise HTTPException(status_code=400, detail=\'gibberish\')\n\n with mock.patch(\'app.api.health.health\', mock_health_function):\n response = client.get(HEALTHCHECK_PATH)\n\n with pytest.raises(HTTPException): # this check passes successfully - my exception is raised\n app.api.health.health()\n\n assert response.status_code != 200 # this check does not pass. The original function was called as if nothing was patched\nRun Code Online (Sandbox Code Playgroud)\n\n尽管测试内部调用了完全相同的函数,但当我到达端点时,API 测试客户端仍然调用原始函数。
\n\n为什么mock.patch测试中不直接调用函数就不能正常工作?
或者也许我应该以某种不同的方式解决我的问题?
\nTho*_*mas 11
您可以使用monkeypatch固定装置来修补您的功能。
首先拉出你想要修补的代码部分:
\nfrom fastapi import FastAPI\n\napp = FastAPI()\n\n\ndef response():\n return "It\'s working \xe2\x9c\xa8"\n\n\n@app.get("/health")\nasync def health():\n return response()\nRun Code Online (Sandbox Code Playgroud)\n然后在测试中使用monkeypatch
\nimport pytest\n\nfrom fastapi import HTTPException\nfrom starlette.testclient import TestClient\n\nfrom app import main\n\n\ndef mocked_response():\n raise HTTPException(status_code=400, detail=\'gibberish\')\n\n\n@pytest.fixture()\ndef client():\n from app.main import app\n\n with TestClient(app) as test_client:\n yield test_client\n\n\ndef test_simple(client, monkeypatch):\n\n monkeypatch.setattr(main, "response", mocked_response)\n resp = client.get("/health")\n assert resp.status_code == 400\n assert resp.json()["detail"] == "gibberish"\nRun Code Online (Sandbox Code Playgroud)\n另一种方法是使用Dependencies和dependency_overrides。\n这可能不适用于所有场景,但对于您给定的用例来说它确实有效。
\nfrom fastapi import FastAPI, Depends\n\napp = FastAPI()\n\n\ndef response():\n return "It\'s working \xe2\x9c\xa8"\n\n\n@app.get("/health")\nasync def health(resp=Depends(response)):\n return resp\nRun Code Online (Sandbox Code Playgroud)\n在您的测试客户端中,您现在可以像这样覆盖依赖项:
\nimport pytest\n\nfrom fastapi import HTTPException\nfrom starlette.testclient import TestClient\n\nfrom app.main import response\n\n\ndef mocked_response():\n raise HTTPException(status_code=400, detail=\'gibberish\')\n\n\n@pytest.fixture()\ndef client():\n from app.main import app\n app.dependency_overrides[response] = mocked_response\n\n with TestClient(app) as test_client:\n yield test_client\n\n\ndef test_simple(client):\n\n resp = client.get("/health")\n\n assert resp.status_code == 400\n assert resp.json()["detail"] == "gibberish"\nRun Code Online (Sandbox Code Playgroud)\n如果您需要向响应函数添加参数,您可以使用闭包模式
\ndef response_closure():\n def response(arg):\n return arg\n return response\n\n\n@app.get("/health")\nasync def health(resp=Depends(response_closure)):\n return resp("It\'s working \xe2\x9c\xa8")\nRun Code Online (Sandbox Code Playgroud)\n