
pytest 在测试发现(test discovery)阶段即执行 database.py 中的顶层连接代码,导致在 conftest.py 的 patch 生效前就尝试访问未存在的数据库文件,引发 sqlite3.operationalerror。根本解法是延迟数据库连接初始化,改用按需创建的上下文管理器或函数式连接工厂。
该问题本质是模块导入时的副作用(import-time side effect):当 test_database.py 导入 from src.database import LOCAL_SESSION 时,Python 会立即执行 database.py 全局作用域中的代码——包括 sqlite3.connect(...) 调用。而此时 conftest.py 中的 @pytest.fixture(autouse=True) 尚未运行(fixture 在测试执行阶段才被注入),Config().table_db_path 仍为原始值(如 "./prod.db"),导致 SQLite 尝试打开一个不存在的生产路径,报错中断测试发现流程。
✅ 正确做法:消除模块级连接,将数据库连接封装为可延迟调用的对象。推荐使用以下任一方案:
方案一:函数式连接工厂(推荐,简洁清晰)
# database.py
import sqlite3
from urllib.request import pathname2url
from .config import Config
def get_local_session():
"""按需创建只读 SQLite 连接,支持测试环境动态配置"""
db_path = Config().table_db_path
dburi = f"file:{pathname2url(db_path)}?mode=r"
return sqlite3.connect(dburi, uri=True)# test_database.py
from src.database import get_local_session
def test_should_return_true_when_read_from_db_is_given():
with get_local_session() as conn: # 每次测试独立连接
cursor = conn.cursor()
cursor.execute("SELECT count(*) AS tot FROM table")
assert cursor.fetchone()[0] == 1方案二:上下文管理器类(适合复杂连接逻辑)
# database.py
import sqlite3
from urllib.request import pathname2url
from .config import Config
class LocalSession:
def __enter__(self):
db_path = Config().table_db_path
dburi = f"file:{pathname2url(db_path)}?mode=r"
self._conn = sqlite3.connect(dburi, uri=True)
return self._conn
def __exit__(self, exc_type, exc_val, exc_tb):
if hasattr(self, '_conn'):
self._conn.close()
def get_local_session():
return LocalSession()# test_database.py
from src.database import get_local_session
def test_should_return_true_when_read_from_db_is_given():
with get_local_session() as conn:
cursor = conn.cursor()
cursor.execute("SELECT count(*) AS tot FROM table")
assert cursor.fetchone()[0] == 1关键注意事项:
- ❌ 禁止在模块顶层(global scope)执行 sqlite3.connect()、open()、requests.get() 等 I/O 操作;
- ✅ conftest.py 中的 autouse=True fixture 仅在测试执行阶段生效,无法影响导入时行为;
- ✅ 若需复用连接(如性能敏感场景),应通过 session-scoped fixture 显式管理生命周期,而非模块变量;
- ✅ 测试数据库路径(如 ./tests/resources/ase_service.db)需确保在测试前已存在或由 fixture 自动初始化。
通过将连接逻辑从“导入即执行”改为“调用即创建”,彻底规避了 pytest 发现阶段的提前失败,同时提升了代码的可测性与健壮性。










