测试环境数据库列缺失异常排查:列存在但测试报错
解决测试时SQLite数据库缺失列的问题
嘿,我太懂这种“明明有列但测试说没有”的崩溃感了!咱们一步步拆解问题,找到根源:
核心原因:MySQL vs SQLite的兼容性差异
你遇到的问题几乎可以肯定是迁移脚本在MySQL和SQLite下的执行结果不一样,加上RefreshDatabase每次测试都会重新跑迁移,导致SQLite环境下根本没创建出目标列:
- 控制器用的是正常的MySQL数据库,迁移脚本在MySQL里能正确生成列;
- 测试用的内存SQLite,它不支持很多MySQL专属的语法/特性,迁移脚本执行时悄悄忽略了这些部分,导致目标列没被创建。
具体排查&解决步骤
1. 先检查你的迁移脚本
打开创建目标表的迁移文件,看看有没有这些SQLite不兼容的写法:
- MySQL专属的列属性:比如
unsigned、auto_increment(SQLite用AUTOINCREMENT但逻辑不同)、ENGINE=InnoDB这类引擎声明; - 特定数据类型:比如旧版SQLite对
json类型支持有限,或者enum类型(SQLite不原生支持enum,会转成text); - 语法细节:比如MySQL允许的
DEFAULT CURRENT_TIMESTAMP在SQLite里可能需要调整写法。
举个例子,如果你的迁移里写了:
$table->integer('user_id')->unsigned();
SQLite不认识unsigned,执行时会直接忽略这个属性,甚至可能导致整列都没创建。
2. 临时切换测试数据库到MySQL验证
把phpunit.xml里的测试数据库改成你的MySQL数据库(临时改,测试完改回去),再跑测试:
如果测试能通过,那100%是SQLite的兼容性问题;如果还是失败,再排查模型、测试代码的问题。
3. 修改迁移脚本实现跨库兼容
针对SQLite和MySQL的差异,调整迁移脚本:
- 用Laravel提供的跨数据库兼容方法:比如用
$table->unsignedBigInteger('user_id')代替手动加unsigned,Laravel会自动适配不同数据库; - 对JSON类型:如果不需要在SQLite里做JSON查询,可以用
$table->text('data')代替json,或者确保你的SQLite版本支持JSON(3.31.0+),并在迁移里用$table->json('data')->nullable(); - 条件判断数据库类型:如果必须用MySQL专属特性,可以在迁移里判断数据库类型:
use Illuminate\Support\Facades\DB; if (DB::getDriverName() !== 'sqlite') { $table->integer('user_id')->unsigned(); } else { $table->integer('user_id'); }
4. 检查RefreshDatabase的执行日志
如果迁移时有报错,RefreshDatabase可能不会在测试输出里显示详细信息,你可以临时在测试里注释掉use RefreshDatabase;,手动跑迁移到SQLite数据库,看控制台有没有报错信息,这样能快速定位迁移里的问题。
额外小提示
如果你的测试不需要用到SQLite的特性,其实可以直接用MySQL做测试数据库(虽然速度慢一点,但能避免兼容性问题);或者用DatabaseMigrations trait代替RefreshDatabase,它只会跑一次迁移而不是每次测试都重置,也能节省时间。
内容的提问来源于stack exchange,提问作者Craig Ward
相关产品推荐
相关产品推荐

