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

Laravel Pest测试因MariaDB自增ID未重置导致断言失败的解决方法

问题描述

我编写了一个Laravel Pest测试,用于验证提交数据创建客户端后控制器的重定向逻辑,测试代码如下:

it('redirects to the clients show page', function () {
    $user = User::factory()->asAdmin()->create();

    auth()->login($user);

    post(route('clients.store'), [
        'name' => 'Test Client',
        'description' => 'Test Description',
        'poc' => 'Test POC',
        'poc_email' => 'Test POC Email',
        'image_path' => 'Test Image Path',
    ])->assertRedirect(route('clients.show', Client::first()));
});

单独运行该测试时一切正常,但运行整个测试套件(之前有多个创建并删除客户端的测试)时,测试失败。原因是每次测试后MariaDB的自增ID未重置,报错信息如下:

Failed asserting that two strings are equal.
Expected :'http://localhost/clients/4'
Actual   :'http://localhost/clients/1'
<Click to see difference>

at vendor/laravel/framework/src/Illuminate/Testing/TestResponse.php:311
at vendor/laravel/framework/src/Illuminate/Testing/TestResponse.php:178
at tests/Feature/Controllers/ClientController/StoreTest.php:87
at vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:177

测试中使用了refresh database trait,出于性能考虑,测试后自增索引不重置是预期行为。环境信息:DB为MariaDB 11.2,PHP 8.3.1,Laravel 10.40。

奇怪的是,新创建客户端的重定向路由指向正确的ID '1',但Client::first()返回的客户端ID为'4',且该测试套件在SQLite下运行正常。

请问如何修改测试,使其不依赖客户端的自增ID来完成验证?

解决方案

方法1:通过唯一字段定位刚创建的客户端

不要依赖Client::first(),而是通过请求中提交的唯一字段(比如name)查询刚创建的客户端,避免受自增ID序列影响:

it('redirects to the clients show page', function () {
    $user = User::factory()->asAdmin()->create();
    auth()->login($user);

    $clientData = [
        'name' => 'Test Client',
        'description' => 'Test Description',
        'poc' => 'Test POC',
        'poc_email' => 'test@poc.example.com',
        'image_path' => 'Test Image Path',
    ];

    $response = post(route('clients.store'), $clientData);
    
    // 通过唯一的name字段查询目标客户端
    $client = Client::where('name', $clientData['name'])->first();
    $response->assertRedirect(route('clients.show', $client));
});

注意:确保name字段在业务中是唯一的,或者改用其他唯一标识字段;同时修正poc_email为合法格式,避免触发验证错误导致测试失败。

方法2:取最新创建的客户端

利用latest()方法按创建时间倒序查询,直接获取刚创建的客户端,不受自增ID顺序影响:

it('redirects to the clients show page', function () {
    $user = User::factory()->asAdmin()->create();
    auth()->login($user);

    $response = post(route('clients.store'), [
        'name' => 'Test Client',
        'description' => 'Test Description',
        'poc' => 'Test POC',
        'poc_email' => 'test@poc.example.com',
        'image_path' => 'Test Image Path',
    ]);

    // 取最新创建的客户端
    $client = Client::latest()->first();
    $response->assertRedirectToRoute('clients.show', $client);
});

这里使用assertRedirectToRoute方法(Laravel 8+支持),可以直接传入路由名称和模型参数,比手动拼接URL更简洁可靠。

方法3:用正则验证重定向URL格式

如果只需要验证重定向到客户端详情页,不需要确认具体客户端,可以用正则表达式匹配URL格式:

it('redirects to the clients show page', function () {
    $user = User::factory()->asAdmin()->create();
    auth()->login($user);

    post(route('clients.store'), [
        'name' => 'Test Client',
        'description' => 'Test Description',
        'poc' => 'Test POC',
        'poc_email' => 'test@poc.example.com',
        'image_path' => 'Test Image Path',
    ])->assertRedirectMatches('/^\/clients\/\d+$/');
});

这种方式不需要查询数据库,但无法验证是否重定向到刚创建的客户端,适合只校验路由类型的场景。

额外说明

出现Client::first()返回旧ID的原因:refresh database trait通过回滚事务清理测试数据,但MariaDB不会回滚自增序列;而SQLite在事务回滚后会重置自增ID,因此SQLite环境下测试正常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 23:47:48