Laravel 8封装通用模型CRUD测试运行触发未定义函数报错的解决
报错根因
自定义测试基类的update()方法中调用了全局函数create(),这个函数是参考的CRUD测试封装方案作者自行定义的测试辅助方法,Laravel框架不存在内置的同名全局函数,你在移植代码时没有将该辅助函数同步到项目中,因此运行测试时直接抛出未定义函数的错误。
这个create()方法的作用是根据传入的模型类名,生成对应模型的测试实例,完全可以用Laravel内置的Factory能力替代,不需要额外定义全局函数。
其他代码存在的问题
- 类Trait重复引入:自定义
TestCase基类已经引入了RefreshDatabaseTrait,子类OperatorTest中又重复引入了相同Trait,属于冗余代码,容易在后续调整数据库事务回滚逻辑时出现冲突。 - 权限校验逻辑顺序错误:当前代码逻辑是先生成测试模型数据、再判断是否存在登录用户,一旦未登录,前面生成的测试数据会已经写入测试数据库,造成数据污染;且你在测试用例中已经提前调用了
signIn()完成登录,基类里的未登录判断分支永远不会触发,逻辑完全失效。 - 密码字段断言逻辑错误:你传入的测试属性包含明文
password参数,而Laravel模型通常会对密码字段自动做哈希处理,数据库实际存储的是哈希值,直接用assertEquals比对明文和数据库值必然断言失败。 - 动态属性未定义:你在测试用例中直接给类动态赋值
$this->attributes,由于基类没有预先声明该属性,后续维护时容易出现属性拼写错误的问题。
修正实现
- 替换基类中不存在的
create()函数调用,改用Laravel原生的Factory生成测试模型:// 原错误代码 // $model = create($model); // 替换为 $model = $model::factory()->create(); - 调整基类的执行顺序,把登录状态校验放在生成测试模型之前,避免未登录场景下产生脏数据;同时优化密码字段的断言逻辑:
protected function update($attributes = [], $model = '', $route = '') { $this->withoutExceptionHandling(); $route = $this->base_route ? "{$this->base_route}.update" : $route; $model = $this->base_model ?? $model; // 先做登录状态校验 if (! auth()->user()) { $this->expectException(\Illuminate\Auth\AuthenticationException::class); } // 校验通过后再生成测试模型 $model = $model::factory()->create(); $response = $this->patchJson(route($route, $model->id), $attributes); tap($model->fresh(), function ($model) use ($attributes) { collect($attributes)->each(function($value, $key) use ($model) { // 跳过密码字段的明文比对,仅校验值存在即可 if ($key === 'password') { $this->assertNotNull($model[$key]); return; } $this->assertEquals($value, $model[$key]); }); }); return $response; } - 移除子类中重复引入的Trait,在基类中预先声明未定义的
$attributes属性:// 自定义TestCase基类中补充预定义属性 protected $attributes = []; // 简化后的OperatorTest类 class OperatorTest extends TestCase { use WithFaker; public function test_user_can_update_an_operator() { $this->setBaseRoute('master.operator'); $this->setBaseModel(\App\Models\Operator::class); $this->signIn(); $this->attributes = [ 'username' => 'test update', 'level' => 1, 'category_id' => 1, 'password' => 'password' ]; $this->update($this->attributes); } }
内容的提问来源于stack exchange,提问作者Muamar Humaidi
相关产品推荐
相关产品推荐

