Laravel集成测试中断言两个Eloquent模型实例相同的最佳方法
在Laravel集成测试中断言两个同类型Eloquent模型对应同一条记录的最佳方式
这个问题我之前做Laravel集成测试时也踩过坑,确实直接用PHPUnit的assertEquals没法准确判断两个Eloquent模型是不是对应同一条数据库记录,原因你也说到了:PHPUnit会递归比较模型的所有属性,包括wasRecentlyCreated这类框架内部维护的属性,哪怕是同一条记录,只要实例创建的场景不同(比如一个是刚save()的,一个是从DB查出来的),这些属性的差异就会导致断言失败;另外主键ID还可能出现字符串和整数类型不匹配的情况,只比ID又没法保证两个模型属于同一个类(比如不同模型表刚好有相同ID的记录)。
下面是几个靠谱的解决方案:
1. 最稳妥的基础方案:同时验证模型类和主键
直接在测试里同时做两个断言:一是验证两个模型属于同一个类,二是验证它们的主键值相等。这里推荐用Eloquent的getKey()方法代替直接取id,因为有些模型的主键不是id(比如用UUID或者自定义主键名),getKey()是通用方法,能自动获取当前模型的主键值:
$this->assertInstanceOf(get_class($modelOne), $modelTwo); $this->assertEquals($modelOne->getKey(), $modelTwo->getKey());
2. 进阶方案:封装自定义断言方法
如果你的测试里经常需要做这种断言,可以在测试基类(比如tests/TestCase.php)里封装一个自定义方法,后续复用起来更方便:
protected function assertSameEloquentModel($expectedModel, $actualModel) { // 先验证模型类一致 $this->assertInstanceOf( get_class($expectedModel), $actualModel, '两个模型不属于同一类型' ); // 再验证主键值相等 $this->assertEquals( $expectedModel->getKey(), $actualModel->getKey(), '两个模型的主键值不匹配' ); }
之后在测试用例里就可以直接调用这个方法:
$this->assertSameEloquentModel($modelOne, $modelTwo);
补充:为什么不推荐其他方式?
- 直接用
assertEquals:如你所说,Eloquent模型包含大量框架内部属性,这些属性在不同实例场景下差异很大,会导致明明是同一条记录却断言失败。 - 只断言主键相等:无法保证两个模型属于同一个类,比如
User模型和Post模型刚好有ID为1的记录,这种情况下断言会错误通过,不符合测试预期。
内容的提问来源于stack exchange,提问作者AndrewMcLagan
相关产品推荐
相关产品推荐

