You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Pytest时Dependency_overrides不生效的问题排查与解决

问题与解决方案

问题情况

运行FastAPI测试时,db.py中执行settings = get_settings()触发报错:

1 validation error for Settings, DATABASE_URL, Field required [type=missing, input_value={}, input_type=dict]

已在conftest.py中重写get_settings函数,但仍出现该错误——因为初始化数据库时会从环境变量读取DATABASE_URL,但测试环境未配置该变量。

原代码文件

main.py

# main.py 
@app.on_event("startup")
def on_startup():    logging.info("Creating database tables")    SQLModel.metadata.create_all(engine)


@app.get("/ping")def ping(settings = Depends(get_settings)):    return settings.DATABASE_URL

db.py

# db.py
from sqlmodel import create_engine, Session
from app.config import get_settings


settings = get_settings() # <- 此处报错
engine = create_engine(settings.DATABASE_URL) 

config.py

# config.py 
from functools import lru_cache
from pydantic_settings import BaseSettings


class Settings(BaseSettings):    DATABASE_URL: str # 在实际容器的环境中设置

@lru_cache
def get_settings():    return Settings()

conftest.py

# conftest.py
import pytest
from fastapi.testclient import TestClient

from app.config import Settings
from app.main import app


def get_settings_override():    DATABASE_TEST_URL = "mssql+pyodbc://sa:Password123@db:1433/master?driver=ODBC+Driver+18+for+SQL+Server&Encrypt=No"    return Settings(DATABASE_URL=DATABASE_TEST_URL)


@pytest.fixture()
def test_app():    with TestClient(app) as test_client:        yield test_client

test_ping.py

# test_ping.py
def test_ping(test_app):    response = test_app.get("/ping")    assert response.status_code == 200    assert response.json() == "pong!"

问题原因

  1. 模块加载时机问题:db.py在模块被导入时就直接执行settings = get_settings(),此时pytest的fixture还未初始化,依赖覆盖逻辑还没生效,调用的是原始get_settings函数——它会从环境变量读取配置,而测试环境没设DATABASE_URL,所以报错。
  2. 未注册依赖覆盖:conftest.py里只定义了get_settings_override,但没有把它注册到FastAPI的依赖覆盖列表中,就算fixture运行了,也不会替换原来的配置函数。

修复方案

1. 延迟数据库引擎初始化

修改db.py,不在模块级别直接初始化settings和engine,改用带缓存的函数获取,确保只有在实际使用时才调用get_settings():

# db.py
from sqlmodel import create_engine, Session
from app.config import get_settings
from functools import lru_cache

@lru_cache
def get_engine():
    settings = get_settings()
    return create_engine(settings.DATABASE_URL)

def get_session():
    engine = get_engine()
    with Session(engine) as session:
        yield session

2. 更新main.py的启动逻辑

在startup事件中调用get_engine()获取引擎,避免直接引用模块级别的engine:

# main.py 
import logging
from fastapi import FastAPI, Depends
from sqlmodel import SQLModel
from app.db import get_engine
from app.config import get_settings

app = FastAPI()

@app.on_event("startup")
def on_startup():    
    logging.info("Creating database tables")    
    engine = get_engine()
    SQLModel.metadata.create_all(engine)

@app.get("/ping")
def ping(settings = Depends(get_settings)):    
    return settings.DATABASE_URL

3. 注册依赖覆盖到conftest.py

在test_app fixture中添加依赖覆盖的注册,确保测试时使用重写后的配置:

# conftest.py
import pytest
from fastapi.testclient import TestClient
from app.config import Settings, get_settings
from app.main import app

def get_settings_override():    
    DATABASE_TEST_URL = "mssql+pyodbc://sa:Password123@db:1433/master?driver=ODBC+Driver+18+for+SQL+Server&Encrypt=No"    
    return Settings(DATABASE_URL=DATABASE_TEST_URL)

@pytest.fixture()
def test_app():    
    # 注册依赖覆盖
    app.dependency_overrides[get_settings] = get_settings_override
    with TestClient(app) as test_client:        
        yield test_client
    # 测试结束后清除覆盖,避免影响其他测试用例
    app.dependency_overrides.clear()

4. 修正测试断言

原测试断言的"pong!"与接口实际返回的数据库URL不符,修改为测试用的URL:

# test_ping.py
def test_ping(test_app):    
    response = test_app.get("/ping")    
    assert response.status_code == 200    
    assert response.json() == "mssql+pyodbc://sa:Password123@db:1433/master?driver=ODBC+Driver+18+for+SQL+Server&Encrypt=No"

内容的提问来源于stack exchange,提问作者RogerKint

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 12:30:22