Laravel Dusk测试:类属性值在后续测试方法中未更新问题咨询
嘿,这个问题我太熟悉了——本质是PHPUnit(Laravel测试的底层框架)的运行机制在搞鬼!
当testCreateNewUser运行时,PHPUnit会创建一个Testing类的全新实例,你在这个实例里修改了$uid为my new value,日志能正确输出是因为你操作的是当前实例的属性。但当testCreateNewUser执行完毕后,这个实例就会被销毁。
等到testRetrieveUser运行时,PHPUnit又会创建一个完全新的Testing实例,这个实例的$uid自然会回到初始的seed值——两个测试方法用的根本不是同一个对象实例,所以之前的修改不会被保留。
几种解决办法
1. 使用静态属性(简单但要注意测试污染)
把$uid改成静态属性,这样它属于类本身,而不是单个实例,不同测试方法的实例就能共享这个值:
class Testing extends DuskTestCase{ protected static $uid = 'seed'; // 改成静态属性 public function testCreateNewUser() { $this->browse(function (Browser $browser) use ($user) { $browser->visit('/')->assertSee('Laravel'); }); self::$uid = 'my new value'; // 用self::访问静态属性 logger(self::$uid); } public function testRetrieveUser() { $uid = self::$uid; logger($uid); // 现在会输出'my new value' } }
⚠️ 注意:静态属性会在所有测试方法间共享,如果你的测试类有多个测试,记得在setUp()或tearDown()方法里重置静态属性的值,避免污染其他测试的结果。
2. 使用PHPUnit的依赖注解(适合强依赖场景)
如果testRetrieveUser必须依赖testCreateNewUser的执行结果,可以用@depends注解让PHPUnit把前一个测试的返回值传递给后一个:
class Testing extends DuskTestCase{ protected $uid = 'seed'; public function testCreateNewUser() { $this->browse(function (Browser $browser) use ($user) { $browser->visit('/')->assertSee('Laravel'); }); $this->uid = 'my new value'; logger($this->uid); return $this->uid; // 返回需要传递的值 } /** * @depends testCreateNewUser */ public function testRetrieveUser($uid) { logger($uid); // 这里会拿到'my new value' } }
⚠️ 注意:这种方式会强制测试按顺序执行,如果testCreateNewUser失败,testRetrieveUser会被直接跳过。而且尽量不要过度依赖这种方式,好的测试应该是独立可运行的。
3. 使用共享存储(灵活且无实例依赖)
可以利用Laravel测试环境的缓存、会话或者临时文件来存储需要共享的值,比如用缓存:
use Illuminate\Support\Facades\Cache; class Testing extends DuskTestCase{ public function testCreateNewUser() { $this->browse(function (Browser $browser) use ($user) { $browser->visit('/')->assertSee('Laravel'); }); Cache::put('test_user_uid', 'my new value'); // 存到缓存 logger(Cache::get('test_user_uid')); } public function testRetrieveUser() { $uid = Cache::get('test_user_uid'); logger($uid); // 会输出'my new value' } // 记得在tearDown里清理缓存,避免影响其他测试 protected function tearDown(): void { parent::tearDown(); Cache::forget('test_user_uid'); } }
这种方式完全脱离了测试类的实例状态,适合复杂的测试场景,而且不会有实例污染的问题。
最后建议
尽量让每个测试方法保持独立,不要依赖其他测试的执行结果——如果testRetrieveUser需要特定的uid,最好在这个方法里自己生成或者初始化,而不是依赖testCreateNewUser的修改。这样你的测试会更健壮,也更容易排查问题。
内容的提问来源于stack exchange,提问作者shirako

