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

测试基于SQLModel的Typer CLI应用时的SQLite临时数据库连接问题与方案咨询

测试基于SQLModel的Typer CLI应用时的SQLite临时数据库连接问题与方案咨询

最近在给用Typer+SQLModel写的CLI应用写测试,想把测试环境的数据库换成pytest临时目录里的SQLite库,结果踩了好几个坑,现在把问题整理出来,求大家帮忙看看怎么解决,或者有没有更优雅的方案。

问题背景

我原本的应用用SQLModel操作本地的database.db,测试时想换成pytest的tmp_path生成的临时数据库,这样测试不会污染真实数据,而且还想用原生sqlite3语句去断言数据库里的内容。但过程中接连遇到两个问题:

遇到的具体问题

  1. 一开始用sqlite3连接临时库时,直接报sqlite3.OperationalError: unable to open database file,后来才发现是连接路径的格式错了;
  2. 修正路径后,又出现sqlite3.OperationalError: no such table: project——我去临时目录里看,db.db确实存在,但是空的!但SQLModel的日志明明显示已经执行了建表和插入操作,这说明SQLModel根本没操作我指定的临时库,还是在操作原来的数据库。

我的代码片段

测试文件

import sqlite3
from typer.testing import CliRunner
from projects import db
from projects.app_typer import app

def temp_db(path):
    db.sqlite_url = f"sqlite:///{path}/db.db"

runner = CliRunner()

# 这个测试是过的,说明sqlite_url确实被修改了
def test_url_replacement(tmp_path):
    temp_db(tmp_path)
    assert db.sqlite_url == f"sqlite:///{tmp_path}/db.db"

# 这个测试就出问题了
def test_add_item_to_db(tmp_path):
    temp_db(tmp_path)
    # 调用CLI的add命令添加项目
    result = runner.invoke(app, ["add", "public", "-n", "Project", "-p", "00-00"])
    # 这里一开始写错了路径格式,后来改成了直接传文件路径
    con = sqlite3.connect(tmp_path / "db.db")
    cur = con.cursor()
    db_entry = cur.execute("SELECT * FROM project").fetchone()
    print(db_entry)

db.py核心代码

from sqlmodel import Field, Session, SQLModel, create_engine, select

# 默认的数据库路径
sqlite_url = "sqlite:///database.db"
# 导入模块时就创建了引擎
engine = create_engine(sqlite_url)

def create_session_and_db():
    # 用上面的引擎创建表
    SQLModel.metadata.create_all(engine)

app_typer.py核心代码

import typer
from projects.db import create_session_and_db
from projects.add import app as add_app

app = typer.Typer(add_completion=False)

# 每次调用CLI都会先执行这个回调,创建表和会话
@app.callback(invoke_without_command=True, no_args_is_help=True)
def main():
    create_session_and_db()

app.add_typer(add_app, name="add", help="Add a project to the DB.")

我自己排查出的点

  1. 第一个问题的原因:SQLModel用的是带sqlite:///前缀的URI格式,但原生sqlite3的connect()方法只需要直接传文件路径(不管是字符串还是pathlib对象),不需要前缀,改成sqlite3.connect(tmp_path / "db.db")就解决了连接问题。
  2. 第二个问题的根源:engine是在导入db.py的时候就创建好了,之后我修改db.sqlite_url只是改了个变量,已经创建的engine实例根本没变化,所以CLI执行create_session_and_db()时,还是用原来的引擎操作原来的数据库,临时库自然是空的。

想请教大家的问题

  1. 我现在的思路是修改全局的sqlite_url来切换数据库,但显然因为引擎初始化时机的问题行不通,有没有更合适的方式在测试中替换数据库?
  2. 应该怎么重构db.py的代码,才能让测试时可以动态切换数据库引擎?比如把引擎的创建改成懒加载,或者用工厂模式?

备注:内容来源于stack exchange,提问作者Jan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 07:04:36