You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在测试中通过Eloquent ORM模拟记录插入及数据库错误?

Laravel 控制器测试相关问题解答

问题代码

控制器代码

namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;

use App\Models\Record;

class SpyController extends BaseController
{
    public function add(Request $request)
    {
        $model = new Record();
        $model->value = $request->get('value');

        return new JsonResponse(['message'=>'Insert Success','record'=>$model],201);
        
    }   
}

测试用例代码

namespace Tests\Controller;

use Illuminate\Testing\Fluent\AssertableJson;
use Laravel\Sanctum\Sanctum;

use App\Models\User;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\DatabaseTestCase;


class SpyRoutesTest extends TestCase
{
   public function addSuccess()
   {
      DB::fake()
      $response = $this->put('/record',['value'=>'lalalala']);
      
      $this->assertStatus(201);
   }
}

问题列表

  • 如何模拟真实的记录插入操作?
  • DB::fake()是否默认判定插入成功?
  • 如何模拟数据库连接超时这类错误?

解答

1. 模拟真实记录插入操作

首先要修正控制器代码:当前控制器仅创建了Record模型实例,但未执行插入数据库的操作(缺少$model->save()或Record::create()逻辑),所以无论测试怎么写,都不会有真实插入行为。

模拟真实插入有两种可行方式:

  • 方式一:使用真实数据库测试
    在测试类中引入RefreshDatabase trait,它会在每个测试前自动迁移数据库,测试后重置数据,保证每次测试环境干净。同时移除DB::fake(),让测试真正执行数据库插入,之后可断言数据库中是否存在目标记录:

    class SpyRoutesTest extends TestCase
    {
        use RefreshDatabase;
    
        public function testAddSuccess()
        {
            $response = $this->post('/record', ['value' => 'lalalala']);
    
            $response->assertStatus(201);
            // 断言数据库中存在指定记录
            $this->assertDatabaseHas('records', ['value' => 'lalalala']);
        }
    }
    

    注意:测试方法名必须以test开头,否则Laravel无法识别为测试用例;新增记录的请求通常用POST而非PUT,需确保路由配置与请求方法对应。

  • 方式二:用DB::fake()模拟并断言操作
    若不想依赖真实数据库,DB::fake()可拦截数据库操作,你可以断言插入操作是否按预期执行,同时确保控制器内真正触发插入逻辑:

    public function testAddSuccess()
    {
        DB::fake();
    
        $response = $this->post('/record', ['value' => 'lalalala']);
    
        $response->assertStatus(201);
        // 断言records表执行了一次插入,且数据匹配
        DB::assertInserted('records', [
            'value' => 'lalalala'
        ]);
    }
    

2. DB::fake()是否默认判定插入成功?

是的,DB::fake()默认会模拟所有数据库操作成功完成,不会主动抛出错误。它的核心作用是拦截真实数据库查询、记录操作执行情况,方便你断言操作是否符合预期,但不会自动模拟失败场景,除非手动配置异常抛出逻辑。

3. 模拟数据库连接超时这类错误

可以通过DB::shouldReceive()或模型的Mock方法,模拟数据库操作抛出PDOException(连接超时通常属于这类异常):

public function testAddWithDatabaseTimeout()
{
    // 模拟Record模型的save方法抛出连接超时异常
    Record::shouldReceive('save')
        ->once()
        ->andThrow(new \PDOException('SQLSTATE[HY000] [2002] Connection timed out'));

    $response = $this->post('/record', ['value' => 'lalalala']);

    // 断言返回自定义的错误状态码
    $response->assertStatus(500);
    // 可额外断言返回的错误信息
    $response->assertJson(['message' => '数据库连接失败']);
}

如果要模拟更底层的数据库连接错误,也可以直接针对DB facade的连接方法做Mock:

DB::shouldReceive('connection')
    ->once()
    ->andThrow(new \PDOException('Connection timed out'));

内容的提问来源于stack exchange,提问作者Dimitrios Desyllas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 13:50:15