assertsee() 不适合 vue/react 渲染内容,visit() 后必须显式等待,loginas() 是跳过登录最稳妥的方式但仅对标准 laravel 认证守卫有效;chromedriver 与 chrome 主版本不匹配会导致 sessionnotcreatedexception;visit() 后应使用 waitfortext() 或 waitfor() 而非 assertsee();type() 触发输入事件,press() 需先聚焦,submit() 才触发表单提交;ci 中需启用 headless 模式并确保 chromedriver 正常监听。

assertSee() 不适合 Vue/React 渲染内容,visit() 后必须显式等待,loginAs() 是跳过登录最稳妥的方式——但只对标准 Laravel 认证守卫有效。
ChromeDriver 和 Chrome 版本不匹配导致 SessionNotCreatedException
这是本地跑 php artisan dusk 时最常卡住的第一步。错误信息通常是 Facebook\WebDriver\Exception\SessionNotCreatedException,根本不是代码写错了,而是底层驱动启动失败。
- 运行
chrome --version查看 Chrome 主版本(如124.0.6367.78) - 运行
chromedriver --version,只比对主版本号(前面的124),小版本不同通常可接受 - 不一致时:删掉
vendor/laravel/dusk/bin/chromedriver-*,再执行php artisan dusk:install - Mac 上用 Homebrew 安装的 Chrome,路径可能在
/opt/homebrew/bin/chrome,需在DuskTestCase::driver()中通过$options->addBinary()显式指定
visit() 后直接 assertSee() 总失败
visit() 只发导航请求,不等 DOM 加载、不等 JS 执行、不等 Vue 组件挂载。此时页面可能还是空的,或只有骨架 HTML。
- 不要写
$browser->visit('/login')->assertSee('邮箱') - 改用
->waitForText('邮箱')(等文本出现在 DOM 文本节点中)或->waitFor('.email-input', 10)(等元素可见,超时 10 秒) -
assertSee()仅检查初始 HTML;assertVisible('@submit-btn')更适合判断按钮是否渲染且可交互 - 若页面由 JS 控制就绪状态(如
window.appReady = true),可用->script('return window.appReady || false')等待信号
表单提交和键盘操作总没反应
Dusk 的 type()、press()、submit() 行为完全不同,混用会导致前端验证不触发、事件未派发、表单不提交。
-
type('email', 'a@b.c')会逐字符输入,并自动触发input和change事件——这是前端验证依赖的 -
press('Enter')仅在焦点位于可编辑元素(如input或textarea)时才生效;必须先->click('input[name="password"]')聚焦 -
submit()才真正触发表单原生提交逻辑(包括submit事件、防重复提交等);press()点按钮只是模拟点击,不一定提交 - 避免用
value()直接设属性——它绕过所有前端事件,表单验证、v-model、React state 都不会更新
CI 环境下 Dusk 白屏或超时
GitHub Actions / GitLab CI 默认无图形界面,Dusk 尝试启动 GUI Chrome 必然失败,且默认不启用 headless 模式。
- 必须在
tests/DuskTestCase.php的driver()方法中强制加--headless=new(Chrome 116+ 推荐)、--no-sandbox、--disable-gpu - GitHub Actions 示例中,不能只装 Node.js,必须显式加一步:
uses: browser-actions/setup-chrome@latest - CI 中
php artisan dusk启动前,确保chromedriver进程已监听http://localhost:9515;若端口被占,可用--server-port=9516指定 - 测试间 session 共享,若多个测试共用同一浏览器实例,建议每个
test方法后调用$browser->quit()或在tearDown()清理 cookies
visit() 自动等待,必须靠 waitFor* 系列方法明确声明依赖。











