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

如何让Laravel Dusk稳定运行数据库种子器?

Laravel Dusk批量测试失败,单独测试正常的问题

问题背景

首页相关测试用例单独运行全部通过,但批量运行时全部失败,怀疑是使用DatabaseTruncation trait时,Dusk处理种子器的方式存在问题。

测试代码示例

<?php

namespace Tests\Browser;

use App\Classes\Settings;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;

class ProductTest extends DuskTestCase
{
    use DatabaseTruncation;

    public bool $seed = true;

    /**
     * A Dusk test example.
     */
    public function testCanSeeProducts(): void
    {
        $this->browse(function (Browser $browser) {
            $browser->loginAs(User::first())
                ->visit('/')
                ->waitFor('@product-card')
                ->assertVisible('@product-card');
        });
    }

    public function testAddToCartVisibility(): void
    {
        nova_set_setting_value('add_to_cart', true);
        $this->browse(function (Browser $browser) {
            $browser->loginAs(User::first())
                ->visit('/')
                ->waitForText('Add to Cart')
                ->assertVisible('@add-to-cart')
                ->logout();
        });
    }

    /*public function testAddToCartInvisible(): void
    {
        nova_set_setting_value('add_to_cart', false);
        $this->browse(function (Browser $browser) {
            $browser->loginAs(User::first())
                ->visit('/')
                ->assertMissing('@add-to-cart')
                ->logout();
        });
    }*/

    /*public function testShowInventory(): void
    {
        nova_set_setting_value('show_inventory', true);
        $this->browse(function (Browser $browser) {
            $browser->loginAs(User::first())
                ->visit('/')
                ->assertVisible('@show-inventory');
        });
    }*/

    /*public function testHideInventory(): void
    {
        nova_set_setting_value('show_inventory', false);
        $this->browse(function (Browser $browser) {
            $browser->loginAs(User::first())
                ->visit('/')
                ->assertMissing('@show-inventory');
        });
    }*/
}

已尝试的操作

  • 每个测试都调用loginAs(),但测试间登录状态未保持,推测是users表被截断导致
  • 在setUp()方法中手动调用seed()
  • 设置public bool $seed = true;

以上操作均未解决问题,期望效果:数据库结构在测试间保持完整,每个测试前执行种子器,测试后截断数据。

报错信息

批量运行前两个测试的报错

  1. 第一个测试报错:
1) Tests\Browser\ProductTest::testCanSeeProducts Facebook\WebDriver\Exception\TimeoutException: Waited 5 seconds for selector [@product-card]. 
  1. 第二个测试报错:
2) Tests\Browser\ProductTest::testAddToCartVisibility ErrorException: Attempt to read property "id" on null 

在setUp()中调用seed()的报错

第一个测试通过,第二个测试未执行种子器,报错:

There was 1 error:

1) Tests\Browser\ProductTest::testAddToCartVisibility
ErrorException: Attempt to read property "id" on null

/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php:266
/Users/ananelson/PhpstormProjects/storefront/app/Models/Vendor.php:22
/Users/ananelson/PhpstormProjects/storefront/app/Observers/ProductObserver.php:21
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php:441
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php:249
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasEvents.php:189
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:1316
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:1138
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:986
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Support/helpers.php:306
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:987
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Support/Traits/ForwardsCalls.php:23
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:2330
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:2342
/Users/ananelson/PhpstormProjects/storefront/database/seeders/ProductSeeder.php:24
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/Util.php:41
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:37
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/Container.php:661
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Seeder.php:184
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Seeder.php:193
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Seeder.php:61
/Users/ananelson/PhpstormProjects/storefront/database/seeders/DatabaseSeeder.php:17
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/Util.php:41
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:37
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/Container.php:661
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Seeder.php:184
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Seeder.php:193
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Console/Seeds/SeedCommand.php:81
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php:155
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Database/Console/Seeds/SeedCommand.php:82
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/Util.php:41
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:37
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Container/Container.php:661
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Console/Command.php:183
/Users/ananelson/PhpstormProjects/storefront/vendor/symfony/console/Command/Command.php:312
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Console/Command.php:153
/Users/ananelson/PhpstormProjects/storefront/vendor/symfony/console/Application.php:1022
/Users/ananelson/PhpstormProjects/storefront/vendor/symfony/console/Application.php:314
/Users/ananelson/PhpstormProjects/storefront/vendor/symfony/console/Application.php:168
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Console/Application.php:102
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Console/Application.php:194
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php:336
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Testing/PendingCommand.php:296
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Testing/PendingCommand.php:481
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithConsole.php:80
/Users/ananelson/PhpstormProjects/storefront/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php:286
/Users/ananelson/PhpstormProjects/storefront/tests/Browser/ProductTest.php:18

解决方案建议

1. 手动控制截断与种子执行顺序

DatabaseTruncation默认在测试后截断数据,但Dusk的进程隔离可能导致种子器执行时机错误。手动在setUp中先截断数据再执行种子:

public function setUp(): void
{
    parent::setUp();
    // 截断业务表,避免影响系统表
    $this->truncateBusinessTables();
    $this->seed();
}

protected function truncateBusinessTables()
{
    $tables = ['users', 'products', 'vendors']; // 根据实际业务补充
    foreach ($tables as $table) {
        \DB::table($table)->truncate();
    }
}

2. 修复种子器依赖问题

从报错看,ProductSeeder第24行调用Vendor的id时对象为空,需确保Vendor已被正确创建:

// 在ProductSeeder中修正
$vendor = \App\Models\Vendor::first() ?? \App\Models\Vendor::factory()->create();
\App\Models\Product::factory()->create(['vendor_id' => $vendor->id]);

3. 显式创建测试数据(推荐)

放弃全局种子器,在每个测试中独立创建所需数据,彻底避免状态污染:

public function testCanSeeProducts(): void
{
    $user = User::factory()->create();
    \App\Models\Product::factory()->count(3)->create();

    $this->browse(function (Browser $browser) use ($user) {
        $browser->loginAs($user)
            ->visit('/')
            ->waitFor('@product-card')
            ->assertVisible('@product-card');
    });
}

4. 禁用进程隔离(谨慎使用)

Dusk默认每个测试在独立进程运行,可能导致数据库状态不同步。可在测试类中添加:

protected static $browsers = 1;
protected $runInSeparateProcess = false;

注意:禁用进程隔离可能导致测试间状态污染,需确保每个测试完全独立。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 06:32:01