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

使用Pytest测试时Mock sqlbuilder.func.FROM_UNIXTIME失效问题排查

解决SQLObject中Mock sqlbuilder.func.FROM_UNIXTIME 生成异常SQL的问题

我明白你遇到的问题:Mock TO_BASE64 能正常工作,但Mock FROM_UNIXTIME 后,WHERE条件里直接变成了 AND (1),而不是期望的strftime语句。这背后的原因是你替换的函数返回的是普通字符串,而SQLObject会把这个字符串当成Python布尔表达式求值(非空字符串在Python中是True,对应SQL里的1),而不是把它当作SQL片段处理。

问题根源

sqlbuilder.func下的函数并不是普通的Python函数,它们是SQLObject用来构建SQL表达式的特殊对象。当你直接用返回字符串的函数替换FROM_UNIXTIME时,SQLObject在解析WHERE条件时,会把你的函数返回值当作Python表达式处理,而非SQL语法的一部分。而TO_BASE64能工作是因为你返回的是原SQLObject列对象(Store.q.sample),这个对象SQLObject能正确识别并转换为SQL。

解决方案

你需要让Mock的FROM_UNIXTIME返回SQLObject能识别的SQL表达式对象,而不是普通字符串。可以用sqlbuilder.CustomSQL来直接封装你的SQL片段,或者用SQLObject原生的函数构建方式来生成对应的SQL。

修改后的测试代码(test_ex1.py)

import pytest
from sqlobject import sqlbuilder
from models import retrieve

try:
    import mock
    from mock import MagicMock
except ImportError:
    from unittest import mock
    from unittest.mock import MagicMock

def TO_BASE64(x):
    return x

def FROM_UNIXTIME(x, y):
    # 使用CustomSQL封装SQL片段,x会被自动转换为对应的SQL列/值
    return sqlbuilder.CustomSQL(
        'strftime("%Y%m%d", datetime(%s, "unixepoch", "localtime"))',
        args=[x]
    )

@mock.patch("sqlobject.sqlbuilder.func.TO_BASE64", TO_BASE64)
@mock.patch("sqlobject.sqlbuilder.func.FROM_UNIXTIME", FROM_UNIXTIME)
def test_retrieve():
    result = retrieve('Some')
    assert result == []

修改后的可运行示例

#! /usr/bin/env python
from sqlobject import *
__connection__ = "sqlite:/:memory:?debug=1&debugOutput=1"

try:
    import mock
    from mock import MagicMock
except ImportError:
    from unittest import mock
    from unittest.mock import MagicMock

class Store(SQLObject):
    name = StringCol()
    sample = BLOBCol()
    createdAt = TimestampCol()

Store.createTable()

DATE_FORMAT = "%Y-%m-%d"

def retrieve(name):
    query = sqlbuilder.Select([
        sqlbuilder.func.TO_BASE64(Store.q.sample),
    ], sqlbuilder.AND(
        Store.q.name == name,
        sqlbuilder.func.FROM_UNIXTIME(Store.q.createdAt, DATE_FORMAT) >= sqlbuilder.func.FROM_UNIXTIME("2018-10-12", DATE_FORMAT)
    ))
    connection = Store._connection
    query = connection.sqlrepr(query)
    print("生成的SQL:", query)
    queryResult = connection.queryAll(query)
    return queryResult

def TO_BASE64(x):
    return x

def FROM_UNIXTIME(x, y):
    return sqlbuilder.CustomSQL(
        'strftime("%Y%m%d", datetime(%s, "unixepoch", "localtime"))',
        args=[x]
    )

for p in [
    mock.patch("sqlobject.sqlbuilder.func.TO_BASE64",TO_BASE64),
    mock.patch("sqlobject.sqlbuilder.func.FROM_UNIXTIME",FROM_UNIXTIME),
]:
    p.start()

retrieve('Some')

mock.patch.stopall()

效果验证

现在运行代码,生成的SQL会和你期望的一致:

SELECT store.sample FROM store WHERE (((store.name) = ('Some')) AND (strftime("%Y%m%d", datetime(store.created_at, "unixepoch", "localtime")) >= strftime("%Y%m%d", datetime('2018-10-12', "unixepoch", "localtime"))))

额外说明

如果不想用CustomSQL,也可以用SQLObject原生的函数构造方式来模拟,比如:

def FROM_UNIXTIME(x, y):
    return sqlbuilder.func.strftime(
        "%Y%m%d",
        sqlbuilder.func.datetime(x, "unixepoch", "localtime")
    )

这种方式更符合SQLObject的原生用法,也能生成正确的SQL。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 20:42:42