Laravel 10 SQLite测试报错:YEAR/MONTH函数不存在,需用TestCase trait?
Laravel 10 测试时SQLite不支持YEAR/MONTH函数的解决办法
问题背景
我在Laravel 10的User模型中写了一个计算月度平均计算次数的方法,在应用视图里运行正常,但执行视图的assertStatus测试时报错。
模型方法代码:
public function numCalculationsAvgMonth(): string { $data = NumberOfCalculations::where('user_id', $this->id) ->selectRaw(' count(id) as data, YEAR(created_at) as year, MONTH(created_at) as month ') ->groupby('year', 'month') ->get() ->avg('data'); return decimals($data, 2); }
测试报错信息:
SQLSTATE[HY000]: General error: 1 no such function: YEAR (Connection: sqlite, SQL: select count(id) as data, YEAR(created_at) as year, MONTH(created_at) as month from "number_of_calculations" where "user_id" = 1 group by "year", "month")
问题:是否需要在TestCase中使用trait来解决该问题?
解决思路与方案
报错本质是:应用运行用的是MySQL(或其他支持YEAR/MONTH函数的数据库),但测试环境用的是SQLite——SQLite原生没有这两个函数,所以测试时触发错误。不需要额外用trait,两种直接方案任选:
方案一:用Laravel兼容多数据库的查询语法
Laravel的查询构建器提供了适配不同数据库的语法支持,把原代码改成这样:
public function numCalculationsAvgMonth(): string { $data = NumberOfCalculations::where('user_id', $this->id) ->selectRaw('count(id) as data') ->selectRaw('year(created_at) as year') ->selectRaw('month(created_at) as month') ->groupBy('year', 'month') ->get() ->avg('data'); return decimals($data, 2); }
Laravel会自动做语法转换:MySQL环境下用YEAR()/MONTH(),SQLite环境下自动换成strftime('%Y', created_at)/strftime('%m', created_at),完美兼容两种环境。
方案二:给SQLite注册自定义函数
如果不想修改模型代码,可以在测试用例的setUp方法里给SQLite注册YEAR和MONTH函数:
use Illuminate\Support\Facades\DB; protected function setUp(): void { parent::setUp(); if (DB::connection()->getDriverName() === 'sqlite') { // 注册YEAR函数 DB::getPdo()->sqliteCreateFunction('YEAR', function($date) { return date('Y', strtotime($date)); }); // 注册MONTH函数 DB::getPdo()->sqliteCreateFunction('MONTH', function($date) { return date('m', strtotime($date)); }); } }
这样SQLite就能识别这两个函数,原模型代码不需要改动,测试就能正常运行。
内容的提问来源于stack exchange,提问作者dgoma108
相关产品推荐
相关产品推荐

