根本原因是Dusk与Workerman网络隔离:Dusk的Chrome实例无法访问独立启动的Workerman服务;需确认Workerman监听0.0.0.0:2345、端口可达、URL协议正确(ws://)、并避免DNS解析干扰,推荐在setUp()中进程级协同启动。

根本原因不是Dusk本身有问题,而是Dusk运行在Laravel内置PHP服务器(php artisan serve)上,而Workerman的WS服务默认监听在另一个端口且未与Dusk共享上下文——两者网络隔离,Dusk测试脚本根本访问不到那个WS地址。
为什么Dusk里ws://localhost:2345连不上Workerman
Dusk启动的是一个独立的Chrome实例,其网络环境不继承PHP CLI进程的本地绑定规则。即使你在本地用php start.php start启了websocket://0.0.0.0:2345,Dusk里的JS仍可能因以下任一原因失败:
- 浏览器沙箱策略阻止
ws://localhost连接(尤其当Dusk用的是--no-sandbox以外的模式) - Workerman实际监听的是
127.0.0.1:2345而非0.0.0.0:2345,导致Dusk Chrome无法从loopback外访问 - Dusk测试运行时,Workerman进程未真正启动(
ps aux | grep Worker查不到worker子进程) - Laravel Mix或Vite开发服务器代理了
/ws路径,把请求劫持走了,根本没发到Workerman
WebSocket connection to 'ws://localhost:2345/' failed: Error in connection establishment怎么定位
这不是证书或协议问题(那是wss才报的),而是纯连接层失败。优先检查这几件事:
- 在Dusk测试机器上执行
telnet localhost 2345:不通 → Workerman没起来或端口错;通 → 问题出在JS或Dusk环境 - 用
ss -tuln | grep :2345确认监听地址是*:2345,不是127.0.0.1:2345 - 检查Dusk测试代码里是否用了
http://开头的URL去连WS——必须是ws://,且端口与Workerman一致 - 如果Workerman跑在Docker里,确认
-p 2345:2345已暴露,且容器内$worker->listen('websocket://0.0.0.0:2345')没写死127.0.0.1
如何让Dusk测试稳定连上本地Workerman WS服务
绕过环境耦合最稳妥的方式是:让Dusk和Workerman在同一个进程生命周期内协作启动。不要依赖全局常驻的Workerman进程。
- 在Dusk的
setUp()里用proc_open()启动Workerman子进程,并捕获PID,测试结束用proc_terminate()杀掉 - Workerman启动参数加
-d后台模式,并确保日志输出到临时文件,便于断言连接是否建立 - Dusk测试中改用
ws://127.0.0.1:2345(不用localhost),避免DNS解析干扰 - 如果用Laravel Sail,直接在
docker-compose.yml里定义Workerman服务并设depends_on,再在Dusk里用服务名(如ws://workerman:2345)连接
最容易被忽略的一点:Dusk默认超时是60秒,但Workerman首次启动+加载框架可能耗时超过这个阈值,导致waitForText或assertSee还没执行,连接就断了。务必在Dusk测试前加显式等待逻辑,比如轮询ws://127.0.0.1:2345是否返回HTTP 101,而不是依赖JS自动重连。


















