如何在Laravel Lighthouse中使用PHPUnit测试GraphQL订阅功能
Lighthouse GraphQL 订阅单元测试实现方案
方案1:单元测试(Mock广播,无需依赖外部WS服务)
这是最适合单元测试的方案,直接基于Laravel自带的广播Mock能力验证逻辑,避免依赖外部服务导致测试不稳定。
核心逻辑:Lighthouse会在订阅绑定的事件触发时,自动生成对应GraphQL查询结果推送到指定频道,你只需要Mock广播门面,断言广播数据符合预期即可。
完整代码示例:
use Illuminate\Support\Facades\Broadcast; public function test_hello_world_build_subscription() { // 开启广播Mock,不会真的推送消息到外部服务 Broadcast::fake(); $graphql = app(\Nuwave\Lighthouse\GraphQL::class); $context = app(\Nuwave\Lighthouse\Schema\Context::class); $result = $graphql->executeQuery(' subscription BuildEvent($input: BuildEventInput!) { buildEvent (input: $input) { message } } ', $context, [ 'input' => [ "uuid" => "0a53b12e-7ae0-4db1-a030-26a7ffdb1902", ] ], ); // 断言订阅初始化成功,正确拿到目标频道 $channel = data_get($result, 'extensions.lighthouse_subscriptions.channel'); $this->assertNotNull($channel); // 触发订阅绑定的事件,传入测试用的消息内容 $testMessage = 'a hello world message'; event(new \App\Events\ExampleEvent($testMessage)); // 断言广播到对应频道的消息内容符合预期 Broadcast::assertSentToChannel( $channel, function ($event, $payload) use ($testMessage) { // Lighthouse推送的payload格式固定,直接取对应字段判断即可 return $payload['data']['buildEvent']['message'] === $testMessage; } ); }
方案2:端到端集成测试(依赖真实WebSocket服务)
如果需要验证完整的推送链路,可以用这套方案,需要提前启动Laravel WebSockets等WS服务,属于集成测试范畴,测试速度相对较慢。
实现步骤:
- 提前在WebSocket配置中开放消息查询API权限
- 触发事件后加短暂延迟,确保消息已经完成推送
- 调用Pusher SDK的API拉取频道消息后做断言
代码片段参考:
// 原有获取channel、实例化Pusher的逻辑不变 $pusher->trigger($channel, 'App\Events\ExampleEvent', 'a hello world message'); // 加0.5秒延迟等待消息推送完成 usleep(500000); // 拉取频道内的消息做断言 $response = $pusher->get('/apps/'.$config['app_id'].'/channels/'.$channel.'/messages'); $messages = json_decode($response->getBody(), true); $this->assertEquals( 'a hello world message', $messages[0]['data']['data']['buildEvent']['message'] );
常见注意事项
- 测试环境
config/lighthouse.php中需要开启订阅:subscriptions.enable设为true - Schema中的
buildEvent订阅必须通过@subscription指令正确绑定到App\Events\ExampleEvent,示例定义:
type Subscription { buildEvent(input: BuildEventInput!): BuildEventPayload @subscription(class: "App\\Events\\ExampleEvent") } type BuildEventPayload { message: String! } input BuildEventInput { uuid: String! }
- 如果是做单元测试优先选方案1,无需启动外部服务,运行速度快,稳定性更高
内容的提问来源于stack exchange,提问作者mowses
相关产品推荐
相关产品推荐

