ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Redwood 组件测试指南:使用 mockGraphQLQuery 与 mockGraphQLMutation 模拟 GraphQL 请求

Redwood 组件测试指南:使用 mockGraphQLQuery 与 mockGraphQLMutation 模拟 GraphQL 请求 后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载在 RedwoodJS 中不依赖真实后端 API 来测试和构建组件是官方推荐的最佳实践。本文以 version-6.x 的官方文档 为核心骨架系统讲解 Redwood 提供的一对核心测试工具mockGraphQLQuery与mockGraphQLMutation从操作名匹配、mock 数据与响应上下文ctx调整到 TypeScript 类型约束、Storybook 全局/局部作用域再到 Cell 的QUERY自动 Mock 机制。读完本文你将能够在不启动 API 服务的情况下为任意组件、Story 和 Cell 编写稳定、可复现的 GraphQL Mock并理解其底层基于 MSW 的实现原理。为什么要 Mock GraphQL 请求前端组件尤其是 Cell 和深度嵌套的组件树在测试或 Storybook 中渲染时通常会发起 GraphQL 查询或变更。如果这些请求真实地打到开发服务器或测试环境会带来三个问题不稳定测试结果依赖后端数据状态数据一变测试就挂慢每次测试都要等待真实的网络往返难覆盖边界错误响应、慢响应、空数据等场景难以在真实 API 上稳定复现。Redwood 通过mockGraphQLQuery匹配查询和mockGraphQLMutation匹配变更让开发者以声明式的方式拦截 GraphQL 请求并返回预设数据。两个函数的参数签名完全一致内部仅根据后缀不同而匹配不同的操作类型operation type——这一设计在源码中有直接体现packages/testing/src/web/mockRequests.ts中两者都委托给同一个mockGraphQL(type, operation, data)内部函数区别仅仅是type传入query还是mutation。核心 API 与基本用法最基本的用法是传入操作名和 mock 数据mockGraphQLQuery(OperationName, (variables, { ctx, req }) { ctx.delay(1500) // 让响应暂停 1.5 秒 return { userProfile: { id: 42, name: peterp, } } })第一个参数是操作名operation name第二个参数可以是对象或函数函数的返回值作为 mock 数据。在 Jest 测试中这两个函数会被自动挂载为全局函数见 jest.setup.js 中的global.mockGraphQLQuery mockGraphQLQuery因此在测试文件里可以直接调用无需额外 import。操作名Operation NameMock 与操作的关联键第一个参数对应 GraphQL 文档中的操作名它是将 mock 数据与某个查询或变更关联起来的唯一依据query UserProfileQuery { /*...*/ } mockGraphQLQuery(UserProfileQuery, { /*... */ })mutation SetUserProfile { /*...*/ } mockGraphQLMutation(SetUserProfile, { /*... */ })操作名必须保持唯一。如果同一个操作名被注册了多个 handler后注册的会覆盖先注册的这正是一节全局 mock 覆盖机制的基础。从源码看mockGraphQL内部调用graphqltype来注册 MSW handler见 mockRequests.tsgraphql.query与graphql.mutation正是 MSW 按操作类型 操作名精确匹配的入口。Mock 数据对象或函数第二个参数支持两种形态直接传对象适用于数据固定、不需要根据变量变化的场景mockGraphQLQuery(UserProfileQuery, { userProfile: { id: 42, name: peterp, }, })传函数函数会收到两个参数variables本次请求携带的 GraphQL 变量和{ ctx, req }。函数返回值作为响应数据mockGraphQLQuery(OperationName, (variables, { ctx }) { ctx.delay(1500) // 暂停 1.5 秒 return { userProfile: { id: 42, name: peterp, } } })借助variables你可以在同一个操作名下根据不同的入参返回不同数据从而模拟按条件查询的行为。req则暴露了底层 MSW 的请求对象可以读取请求头、请求原文等DataFunction的类型签名定义在 mockRequests.ts。用 ctx 调整响应status、delay、errors当第二个参数是函数时ctx对象提供了三个内置方法用于对响应做精细化调整方法作用典型场景ctx.status(code: number, text?: string)设置 HTTP 响应状态码可附带状态文本模拟 404、500 等错误状态ctx.delay(numOfMS)延迟响应指定毫秒数模拟慢网络、验证 loading 态ctx.errors(e: GraphQLError[])在响应中返回 GraphQL 错误数组模拟服务端校验失败、业务错误设置 HTTP 状态码mockGraphQLQuery(OperationName, (_variables, { ctx }) { ctx.status(404) })延迟响应mockGraphQLQuery(OperationName, (_variables, { ctx }) { ctx.delay(1500) // 暂停 1.5 秒 return { id: 42 } })配合 Redwood 的 Suspense / Cell 的 loading 态可以稳定断言数据加载中的 UI 表现。返回 GraphQL 错误mockGraphQLQuery(OperationName, (_variables, { ctx }) { ctx.errors([{ message: Uh, oh! }]) })从源码看这些方法并非直接透传mockGraphQL会包装 MSW 的原始ctx把status、delay、errors等方法的返回值逐一捕获进responseTransforms数组最后统一拼进最终的res()调用见 mockRequests.ts。这意味着你可以同时使用多个ctx方法例如先ctx.status(500)再ctx.errors([...])它们会按顺序叠加生效。TypeScript为 Mock 提供强类型Redwood 会自动生成 GraphQL 相关类型默认位于types/graphql你可以直接把它们传给 Mock 函数获得完整的类型检查import type { UserProfileQuery, UserProfileQueryVariables } from types/graphql mockGraphQLQueryUserProfileQuery, UserProfileQueryVariables(UserProfileQuery, { /*... */ })第一个泛型参数对应查询/变更的返回数据结构第二个泛型参数对应变量结构。也可以手动传入自定义类型适用于类型尚未生成或需要刻意构造非标准数据的情况mockGraphQLQuery{ userProfile: { id: number, name: string, } }(UserProfileQuery, { /*... */ })源码层面mockGraphQLQuery与mockGraphQLMutation的泛型默认值分别为Recordstring, unknown与Recordstring, any见 mockRequests.ts因此即使不显式传入类型也不会编译报错但传入类型后编辑器会针对variables和返回数据给出自动补全与错误提示。全局 Mock vs 局部 Mock全局 Mock.mock.js文件把 mock 请求放在命名为name.mock.js或.mock.ts、.mock.jsx、.mock.tsx的文件中即可在Storybook 中全局生效对所有 Story 可用为什么需要全局 Mock在 React 中一个组件常常内部嵌套了深层组件而这些嵌套组件会各自发起 GraphQL 查询或变更。如果每个 Story 都要手动 Mock 一遍这些请求会非常痛苦和繁琐。全局 Mock 一次注册、处处可用。局部 MockStory 内部调用在 Story 内部直接调用mockGraphQLQuery或mockGraphQLMutation则是局部作用域的并且会覆盖同名的全局 Mock。官方建议始终优先从全局 Mock 起步仅在个别 Story 需要特殊数据时再局部覆盖。该机制在 Jest 测试环境中同样成立jest.setup.js在beforeAll阶段自动扫描并加载所有 Cell Mock随后启动 MSW 服务在afterEach中调用setupRequestHandlers()重置 handlers保证用例之间互不污染见 jest.setup.js。Mocking Cell 的 QUERY.mock.js与 standard 导出Redwood 的 Cell 是数据驱动的核心模式每个 Cell 组件都导出一个QUERY。要 Mock Cell 的查询只需要在Cell 所在目录中创建一个同名的.mock.js文件并导出一个名为standard的值export const QUERY gql query UserProfileQuery { userProfile { id } } // UserProfileCell/UserProfileCell.mock.js export const standard { userProfile: { id: 42 } }standard的值就是该 Cell 的QUERY返回的 mock 数据。因此有一个重要的维护约束修改了QUERY就必须同步修改 mock 数据否则会出现查询请求了name字段但 mock 数据里没有之类的字段缺失问题export const QUERY gql query UserProfileQuery { userProfile { id name } } // UserProfileCell/UserProfileCell.mock.js export const standard { userProfile: { id: 42, name: peterp, } }幕后机制Behind the scenesRedwood 会把standard的值作为mockGraphQLQuery的第二个参数传入。这一幕后机制比文档描述的更为智能——它是由 Babel 插件在编译期自动完成的而非运行时手动调用。babel-plugin-redwood-mock-cell-data插件会找到 Cell 目录下导出了standard的.mock.*文件解析同目录 Cell 源码中QUERY的 GraphQL 文档提取出操作名把export const standard {...}重写为mockGraphQLQuery(operationName, standard)的形式若 Cell 还导出了afterQuery则自动把 mock 数据包进afterQuery(...)再返回。对应的实现见 babel-plugin-redwood-mock-cell-data.ts其转换规则在注释中明确列出必须是*.mock.[ts,js]文件、必须有名为standard的具名导出、必须与 Cell 相邻、Cell 必须有QUERY导出且其操作名可解析。在真实项目夹具中可以看到这类文件的典型形态例如__fixtures__/example-todo-main/web/src/components/NumTodosCell/NumTodosCell.mock.js。源码级原理MSW 与懒注册队列Redwood 的 GraphQL Mock 底层基于MSWMock Service Worker在JestNode环境下使用msw/node的setupServer在Storybook浏览器环境下使用setupWorker入口函数startMSW(target, options)会根据目标环境选择对应的 MSW 实现见 mockRequests.ts。一个值得注意的细节是懒注册队列开发者可以在 MSW 服务启动之前就调用mockGraphQLQuery/mockGraphQLMutation。此时 handler 不会丢失而是被暂存在REQUEST_HANDLER_QUEUE队列中等startMSW启动服务后再统一排空注册见 mockRequests.ts。registerHandler会在服务未启动时追加到队列服务已启动时直接调用SERVER_INSTANCE.use(handler)。此外源码还提供了一些文档之外但可直接使用的能力responseEnhancer第三参数mockGraphQLQuery/mockGraphQLMutation还接受一个可选的响应增强参数once表示仅拦截一次、networkError表示模拟网络错误在 MockHandlers.test.tsx 中可以看到once的实际用法mockCurrentUser通过注册__REDWOOD__AUTH_GET_CURRENT_USER这个特殊操作名的 Mock 来模拟当前登录用户便于测试依赖useAuth()的组件。实践建议能全局就全局把通用的查询/变更 Mock 放进.mock.js文件避免每个 Story 重复注册只有特殊场景才在 Story 内局部覆盖。保持操作名唯一且语义清晰操作名既是 GraphQL 规范要求也是 Mock 匹配的键命名混乱会导致 Mock 互相覆盖、排查困难。QUERY 与 standard 同步演进Cell 增加字段后立即同步.mock.js否则测试与 Storybook 会出现静默的字段缺失。善用 ctx 覆盖边界场景用ctx.delay验证 loading 态、用ctx.errors验证失败态、用ctx.status验证 HTTP 错误分支让组件测试覆盖真实网络环境下难以稳定复现的路径。充分利用生成的类型优先从types/graphql引入自动生成的类型来约束 mock 数据编译器能在数据与查询不同步时第一时间给出提示。通过mockGraphQLQuery与mockGraphQLMutationRedwood 将不依赖 API 的前端开发与测试变成了开箱即用的能力它既是单元测试的稳定数据源也是 Storybook 中独立构建组件的基础设施底层由 MSW 统一接管网络层让开发者专注于组件在该数据下如何渲染这一核心问题。赞分享后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载相关推荐Redwood 组件测试指南用 mockGraphQLQuery / mockGraphQLMutation 模拟 GraphQL 请求Redwood 组件测试指南用 mockGraphQLQuery / mockGraphQLMutation 模拟 GraphQL 请求 测试与构建组件时不后端前端Web框架开发工具Redwood 中 Mock GraphQL 请求用 mockGraphQLQuery / mockGraphQLMutation 测试组件Redwood 中 Mock GraphQL 请求用 mockGraphQLQuery / mockGraphQLMutation 测试组件 在 Redwoo后端前端Web框架开发工具Redwood 测试与 Storybook 中的 GraphQL 请求 Mock 完整指南mockGraphQLQuery 与 mockGraphQLMutation 实战Redwood 测试与 Storybook 中的 GraphQL 请求 Mock 完整指南mockGraphQLQuery 与 mockGraphQLMuta后端前端Web框架开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表