php artisan dusk 在 ci 中报错找不到 chromedriver,因 dusk 仅声明 dev 依赖而未管理运行时依赖(如 facebook/webdriver 和 chrome/chromedriver),且默认自动注册驱动、未适配 ci 环境的路径、权限、沙箱限制。

为什么 php artisan dusk 在 CI 环境里直接报错找不到 ChromeDriver?
因为 Laravel Dusk 默认在 require-dev 里声明了 laravel/dusk,但没管它的运行时依赖——比如 facebook/webdriver 和实际的浏览器二进制(Chrome / Chromedriver)。很多人把 chromedriver 丢进 vendor/bin/ 就以为完事了,结果 CI 容器一跑就崩:找不到可执行文件、版本不匹配、或权限被拒。
-
laravel/dusk本身只是测试封装层,facebook/webdriver才是真正发 HTTP 请求控制浏览器的客户端,它必须存在且可用 - ChromeDriver 必须和系统已装 Chrome 版本对得上;Dusk 默认调用
chromedriver命令,不是绝对路径,所以得确保它在$PATH里 - 本地开发可能靠 brew / apt 装过全局 Chromedriver,但 CI 是干净镜像,没这层“侥幸”
怎么让 Dusk 只在需要时才加载 WebDriver 驱动?
Dusk 的 DuskServiceProvider 默认一启动就注册驱动,哪怕你只跑单元测试也绕不开。真隔离,得从服务提供者入手——关掉自动注册,改用手动触发。
- 在
phpunit.xml里加环境变量:APP_ENV=dusk,再配DB_CONNECTION=sqlite这类轻量配置 - 注释掉
config/app.php中Laravel\Dusk\DuskServiceProvider::class这一行 - 在
tests/DuskTestCase.php的setUpBeforeClass()里手动启动:static::startChromeDriver(); - 这样只有真正执行 Dusk 测试时,才会去检查
chromedriver是否存在、是否可执行
如何避免把 Chromedriver 提交进 Git 或污染 vendor?
别把二进制文件放项目里,也别用 composer require --dev laravel/dusk 顺手拉下所有东西。精准控制的关键是分层安装。
- 只在
require-dev里保留laravel/dusk和facebook/webdriver,删掉任何带chromedriver字样的包(比如robbiep/cloudconvert-php这种无关依赖里偷偷带的) - CI 脚本里用
curl -L -o /usr/local/bin/chromedriver https://chromedriver.storage.googleapis.com/125.0.6422.141/chromedriver_linux64.zip下载解压,并chmod +x - 本地开发用
export DUSK_DRIVER_PATH=/usr/local/bin/chromedriver显式指定路径,绕过 Dusk 自动探测逻辑
为什么 --env=dusk 不能解决依赖隔离问题?
这个参数只影响 Laravel 的配置加载(比如切到 phpunit.dusk.xml),跟 Composer 的 autoloader 和扩展加载完全无关。即使 APP_ENV=dusk,facebook/webdriver 类依然会在所有测试中被 autoload —— 只要它在 vendor/autoload.php 里被声明过。
- 真正起作用的是
autoload-dev的 PSR-4 映射范围:确保tests/下的类不被主应用加载,反过来也一样 - 如果非要用
--env控制行为,得配合自定义AppServiceProvider的register()方法,用app()->environment('dusk')包住$this->app->register(DuskServiceProvider::class) - 但更干净的做法是:Dusk 测试单独用一个
phpunit.dusk.xml,里面bootstrap指向tests/DuskTestCase.php,彻底和主测试套件隔开
vendor。最容易被忽略的一点:Dusk 启动的 Chrome 进程默认不加 --no-sandbox,在 Docker 里会直接 crash,这个参数必须显式传给 ChromeOptions,而不是指望环境变量。











