如何让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) Tests\Browser\ProductTest::testCanSeeProducts Facebook\WebDriver\Exception\TimeoutException: Waited 5 seconds for selector [@product-card].
- 第二个测试报错:
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
相关产品推荐
相关产品推荐

