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

Laravel项目PHPUnit测试时出现SQL语法错误求助

Laravel测试时SQL语法错误:CONCAT/UPPER/RIGHT函数兼容性问题

问题背景

我在Laravel项目中实现了一个表单查询逻辑:通过拼接用户名后3位(转为大写)与ID生成编码,以此查询表单。应用运行时该查询正常,但PHPUnit测试时出现SQL语法错误:

SQLSTATE[HY000]: General error: 1 near "(": syntax error (SQL:
    select * from "forms"
    where "factual_checkout_at" is null
    and "plan_checkout_at" >= ?
    and CONCAT(UPPER(RIGHT(name, 3)), id) = ?
    and "forms"."deleted_at" is null and "forms"."type" in (?, ?, ?) limit 1
)

查询实现代码如下:

$form = Form::whereNull('factual_checkout_at')
            ->where('plan_checkout_at', '>=', Carbon::today())
            ->whereRaw('CONCAT(UPPER(RIGHT(name, 3)), id) = ?', $code)
            ->firstOrFail();
return new FormResource($form);

问题根源

这个问题其实很常见——你的测试环境和运行环境使用的数据库不一致!大概率你开发/生产用的是MySQL(或PostgreSQL),但PHPUnit测试用的是SQLite。

两者的SQL函数存在关键差异:

  • MySQL的RIGHT(name, 3)用来取字符串末尾3位,但SQLite并不支持RIGHT()函数,它需要用SUBSTR(name, -3)来实现相同效果
  • 虽然SQLite 3.33.0+开始支持CONCAT(),但如果你的测试环境SQLite版本较低,也可能触发问题,但这里核心问题是RIGHT()函数的兼容性

解决办法

有两种可行的方案,你可以根据自己的情况选择:

方案1:编写兼容多数据库的SQL语句

用SQLite和MySQL都支持的语法替代RIGHT(),修改whereRaw部分:

$form = Form::whereNull('factual_checkout_at')
    ->where('plan_checkout_at', '>=', Carbon::today())
    ->whereRaw('CONCAT(UPPER(SUBSTR(name, -3)), id) = ?', [$code])
    ->firstOrFail();

SUBSTR(name, -3)在MySQL和SQLite中都表示取name字段的最后3个字符,完美兼容。

如果你想更贴合Laravel的查询构造器风格,也可以拆分成链式调用:

$form = Form::whereNull('factual_checkout_at')
    ->where('plan_checkout_at', '>=', Carbon::today())
    ->where(function ($query) use ($code) {
        $query->whereRaw('CONCAT(UPPER(SUBSTR(name, -3)), id) = ?', [$code]);
    })
    ->firstOrFail();

方案2:统一测试环境的数据库

直接把PHPUnit测试用的数据库改成和开发环境一致的(比如MySQL),这样就不需要修改代码了。你只需要在项目根目录的phpunit.xml文件中调整数据库配置:

<php>
    <env name="DB_CONNECTION" value="mysql"/>
    <env name="DB_DATABASE" value="your_test_database_name"/>
    <env name="DB_USERNAME" value="your_db_username"/>
    <env name="DB_PASSWORD" value="your_db_password"/>
</php>

验证

修改完成后重新运行PHPUnit测试,语法错误应该就会消失了。本质上这就是不同数据库的SQL语法兼容性问题,只要保证测试和运行环境的数据库一致,或者使用兼容的语法就能解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:12:51