graphql-js订阅集成测试异常:GraphiQL正常但测试失败
我帮你梳理下几个关键问题,一个个来解决:
1. 集成测试中execGraphQL未初始化,导致Mutation根本没执行
看你的测试代码,let execGraphQL;之后直接调用await execGraphQL(...),但这个变量完全没赋值,会直接抛出TypeError: execGraphQL is not a function,Mutation都没跑,订阅自然收不到正确的payload。
修复代码:
用chai-http实现一个发送GraphQL请求的方法:
execGraphQL = async (query) => { return chai.request(server) .post('/graphql') .send({ query }); };
然后再调用await execGraphQL(...)执行Mutation。
2. Apollo Client配置混用新旧API,导致订阅链路异常
你同时设置了旧版的networkInterface和新版的link,这会让Apollo Client的网络层逻辑混乱,无法正确接收订阅事件。在Apollo Client 2.x+版本,应该用WebSocketLink和HttpLink组合,通过split区分订阅和查询/突变的请求链路。
修复代码:
const { WebSocketLink } = require('apollo-link-ws'); const { split } = require('apollo-link'); const { getMainDefinition } = require('apollo-utilities'); // 创建WebSocket链接处理订阅 const wsLink = new WebSocketLink({ uri: GRAPHQL_ENDPOINT, options: { reconnect: true }, webSocketImpl: ws }); // 创建HTTP链接处理查询和突变 const httpLink = new HttpLink({ uri: 'http://localhost:3000/graphql' }); // 自动选择请求链路:订阅用WS,其他用HTTP const link = split( ({ query }) => { const definition = getMainDefinition(query); return definition.kind === 'OperationDefinition' && definition.operation === 'subscription'; }, wsLink, httpLink ); // 初始化Apollo Client apollo = new ApolloClient({ link, cache: new InMemoryCache() });
3. 订阅的filter函数里错误调用pubSub.subscribe
你在withFilter的过滤逻辑里调用了pubSub.subscribe(info.fieldName, null, subscriptionPayload),这完全是逻辑错误——pubSub.subscribe是服务端用来订阅事件的方法,而这里的subscribe函数本身就是处理客户端订阅的入口,filter函数只负责判断是否要把当前payload推给客户端,不该在这里做订阅操作,这会导致事件订阅混乱,返回错误的payload。
修复方案:
直接删除这行代码,只保留过滤逻辑(如果需要的话):
subscribe: withFilter( () => pubSub.asyncIterator(modelName), (payload, variables, context, info) => { // 这里只做过滤判断,比如匹配创建/更新/删除操作 const { create } = variables.input; // 假设你的payload里有action字段标识操作类型,按需调整 // return create && payload.action === 'CREATE'; return true; // 先保持返回true,确保能收到数据 } ),
4. 订阅resolve函数的字段映射错误
你的订阅查询里是Customer { customer { name } },但resolve函数返回的是object: payload.data,这会导致客户端拿到的customer字段为undefined。另外,clientSubscriptionId应该从客户端传入的args.input里取,而不是从服务端发布的payload里拿。
修复代码:
resolve(payload, args, context, info) { // 从客户端订阅参数里取clientSubscriptionId const { clientSubscriptionId } = args.input; const { object } = payload; // 服务端发布的事件数据 const where = object?.where; const type = object?.type; const target = object?.target; return Promise.resolve(subscribeAndGetPayload(payload, args, context, info)) .then(result => ({ clientSubscriptionId, where, type, target, customer: result.data, // 和查询里的字段名保持一致 })); }
5. 调整测试断言逻辑
修复完以上问题后,订阅收到的payload结构会是:
{ data: { Customer: { customer: { name: "Atif 21" } } } }
所以测试断言要改成:
const result = await subscriptionPromise; expect(result.data.Customer.customer.name).to.equal("Atif 21");
内容的提问来源于stack exchange,提问作者M Atif

