万步健康app
77.51MB · 2025-09-10
FastAPI 的依赖注入系统(Dependency Injection)是其核心特性之一,它通过自动解析和管理组件间的依赖关系,极大提高了代码的可测试性和可维护性。
? 什么是 Mock?
Mock(模拟对象)是测试中创建的虚拟对象,用于替代真实依赖(如数据库连接)。核心作用:
from fastapi import Depends, FastAPI
# 真实数据库连接函数
def get_db():
print("Connecting to real database...")
return "RealDB Connection"
app = FastAPI()
@app.get("/")
def read_data(db: str = Depends(get_db)):
return {"database": db}
当编写单元测试时,直接调用真实数据库会引发三大问题:
? 典型案例:用户注册接口的测试
FastAPI 的依赖系统本质是层级解析器:
Route → Controller → Service → DB
flowchart TD
A[路由接口] --> B[控制器]
B --> C[服务层]
C --> D[数据库连接]
通过 Depends()
声明的依赖都是接口可替换的:
from fastapi import Depends
# 真实数据库连接
def real_db():
return PostgreSQLConnection()
# 测试用Mock数据库
def mock_db():
return InMemoryDB()
# 在测试中可动态替换
@app.get("/users")
def get_users(db = Depends(real_db)): # 切换为 mock_db 即可覆盖
使用 unittest.mock.patch
替换目标函数:
from unittest.mock import patch
from fastapi.testclient import TestClient
client = TestClient(app)
def test_read_data():
# 模拟 get_db 函数返回指定值
with patch("main.get_db", return_value="MockDB"):
response = client.get("/")
assert response.json() == {"database": "MockDB"}
处理 yield
型依赖(如数据库会话):
from contextlib import contextmanager
@contextmanager
def mock_db_session():
print("Start mock session")
yield "MockSession"
print("Cleanup mock")
# 测试中覆盖依赖
app.dependency_overrides[get_db] = mock_db_session
def test_with_session():
response = client.get("/")
assert "MockSession" in response.text
结合 Pydantic 实现类型安全的 Mock:
from pydantic import BaseModel
class MockUser(BaseModel):
id: int = 1
name: str = "Test User"
def test_user_create():
# 创建符合接口契约的Mock数据
mock_data = MockUser().dict()
with patch("user_service.create_user", return_value=mock_data):
response = client.post("/users", json={"name": "Alice"})
assert response.json()["id"] == 1 # 验证模型字段
层级 | 模拟对象 | 工具示例 |
---|---|---|
路由层 | HTTP 响应 | TestClient |
服务层 | 业务逻辑 | unittest.mock |
存储层 | 数据库 | SQLAlchemy-mock |
通过 app.dependency_overrides
实现全局替换:
def override_get_db():
return "GlobalMockDB"
app.dependency_overrides[get_db] = override_get_db
使用 pytest 高效管理 Mock 生命周期:
import pytest
from fastapi import FastAPI
@pytest.fixture
def mock_app():
app = FastAPI()
app.dependency_overrides[get_db] = lambda: "PytestMockDB"
return app
def test_with_fixture(mock_app):
client = TestClient(mock_app)
response = client.get("/")
assert "PytestMockDB" in response.text
为什么单元测试中不能直接使用真实数据库?
A. 会导致测试数据污染生产环境
B. 数据库查询会拖慢测试速度
C. 无法模拟异常情况
D. 以上全部
如何快速验证依赖注入是否被正确覆盖?
A. 查看日志输出
B. 在 Mock 函数中添加 print 语句
C. 断言接口返回的特定标识
D. 使用调试器逐步执行
以下哪种场景最适合使用 Pydantic 模型 Mock?
A. 模拟 HTTP 超时错误
B. 验证接口返回的数据结构
C. 替换第三方支付网关
D. 生成测试用的 JWT Token
答案:D
答案:C
答案:B
AttributeError: module 'unittest.mock' has no attribute 'patch'
# 正确导入方式
from unittest.mock import patch # Python >=3.3
DependencyOverrideError: No dependency found for <function get_db>
Depends()
app.dependency_overrides[module.get_db] = ...
TypeError: object NoneType can't be used in 'await' expression
# 为异步函数返回 awaitable 对象
async def mock_async_db():
return "AsyncMock"
typing.AsyncGenerator
明确异步依赖类型环境要求
Python >=3.7
fastapi==0.103.1
pydantic==2.4.2
httpx==0.25.0
pytest==7.4.2