You need to enable JavaScript to run this app. Laravel 8封装通用模型CRUD测试运行触发未定义函数报错的解决
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 8封装通用模型CRUD测试运行触发未定义函数报错的解决

报错根因

自定义测试基类的update()方法中调用了全局函数create(),这个函数是参考的CRUD测试封装方案作者自行定义的测试辅助方法,Laravel框架不存在内置的同名全局函数,你在移植代码时没有将该辅助函数同步到项目中,因此运行测试时直接抛出未定义函数的错误。
这个create()方法的作用是根据传入的模型类名,生成对应模型的测试实例,完全可以用Laravel内置的Factory能力替代,不需要额外定义全局函数。

其他代码存在的问题

  • 类Trait重复引入:自定义TestCase基类已经引入了RefreshDatabaseTrait,子类OperatorTest中又重复引入了相同Trait,属于冗余代码,容易在后续调整数据库事务回滚逻辑时出现冲突。
  • 权限校验逻辑顺序错误:当前代码逻辑是先生成测试模型数据、再判断是否存在登录用户,一旦未登录,前面生成的测试数据会已经写入测试数据库,造成数据污染;且你在测试用例中已经提前调用了signIn()完成登录,基类里的未登录判断分支永远不会触发,逻辑完全失效。
  • 密码字段断言逻辑错误:你传入的测试属性包含明文password参数,而Laravel模型通常会对密码字段自动做哈希处理,数据库实际存储的是哈希值,直接用assertEquals比对明文和数据库值必然断言失败。
  • 动态属性未定义:你在测试用例中直接给类动态赋值$this->attributes,由于基类没有预先声明该属性,后续维护时容易出现属性拼写错误的问题。

修正实现

  1. 替换基类中不存在的create()函数调用,改用Laravel原生的Factory生成测试模型:
    // 原错误代码
    // $model = create($model);
    // 替换为
    $model = $model::factory()->create();
    
  2. 调整基类的执行顺序,把登录状态校验放在生成测试模型之前,避免未登录场景下产生脏数据;同时优化密码字段的断言逻辑:
    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;
    }
    
  3. 移除子类中重复引入的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.24 15:36:04