资讯动态

Refine v5 测试指南:理解可测试性设计,用 Cypress 为内部工具编写端到端测试

发布时间:2026/9/12 14:49:19 来源:尧图企业网站定制
Refine v5 测试指南理解可测试性设计用 Cypress 为内部工具编写端到端测试【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refineRefine 官方文档将测试策略划分为单元测试与端到端测试两个层面框架自身的组件与 hooks 已由维护者完成单元测试因此推荐开发者把精力集中在应用级别的端到端测试上。本文将基于 Refine v5 官方测试指南documentation/docs/further-readings/testing.md结合仓库中真实的 Cypress 测试基础设施cypress/ 目录系统讲解 Refine 的可测试性设计理念、单元测试与 E2E 测试的边界划分并给出可直接复用的 Cypress 测试编写范式。Refine 的测试设计哲学小代码块与独立可测Refine 的组件和 hooks 是由小块代码small pieces of code构成的。每个组件或 hook 都被刻意设计为可测试的并且彼此独立工作——这意味着单个 hook 不依赖页面上下文即可被单独调用和断言单个组件不依赖外部全局状态即可渲染和交互数据 provider、auth provider、路由等能力通过明确的接口注入天然易于在测试中替换或拦截。这种小而独立的设计正是官方测试策略的底层支撑既然框架内部每一个单元都已被验证为可独立工作开发者就没有必要再为框架本身编写重复的单元测试。从仓库源码也能印证这一点框架核心包 packages/core 与各 UI 集成包packages/antd、packages/mui、packages/chakra-ui、packages/mantine 等均自带测试文件与快照如 packages/inferencer 下的*.snap说明框架层测试由维护者在包内完成与官方文档的表述一致。单元测试写什么不写什么官方指南给出的单元测试边界非常明确你不需要为 Refine 编写单元测试因为 Refine 已经被其维护者测试过了。但你可以在自己的代码中编写单元测试helper、definitions 等。翻译成实操原则就是层面是否建议写单元测试说明Refine 组件 / hooks 的使用方式不需要框架层已被维护者测试覆盖重复测试徒增维护成本业务 helper、工具函数、类型定义建议这是你完全自有的逻辑值得用最小成本锁定行为复杂的数据转换、权限判断逻辑建议纯函数最容易测试收益最高页面 / 组件 / 用户流程不建议用单元测试交给端到端测试见下文换句话说单元测试只服务于你自己的纯逻辑而所有涉及渲染、路由跳转、表单提交、通知反馈的用户可见行为都应交给端到端测试去覆盖。端到端测试官方强烈推荐的方向文档明确写道我们强烈建议你为自己的应用程序编写端到端测试。Refine 以 Cypress 框架作为示例你可以自由选择任何你喜欢的测试框架。内部工具、管理面板与 B2B 应用的价值在于整个流程跑得通登录 → 列表 → 创建 → 编辑 → 删除 → 反馈通知。这类跨组件、跨路由、跨请求的完整链路只有端到端测试才能真实还原。值得注意的是文档中原本指向的示例with-cypress如今在仓库中已是一个已删除的占位说明examples/with-cypress/README.md 明确写道This example has been deleted because many examples below have been support Cypress test.也就是说Cypress 测试能力已经被下沉到了大量示例仓库本身读者可以直接在 examples/base-antd、examples/base-chakra-ui、examples/base-mantine 等示例上运行测试无需再依赖独立的 with-cypress 示例。仓库中的 Cypress 测试基础设施解剖仓库根目录的 cypress/ 是整个项目的 E2E 测试中枢其结构清晰、分层合理是学习 Refine 官方测试范式的第一手资料。1. 全局配置一次看懂官方如何调教 Cypresscypress/cypress.config.ts 是官方测试的全局配置关键项及含义如下配置项值作用projectIdsq5j3e关联 Cypress Cloud 的仪表盘项目 IDretries.runMode3命令行运行时失败最多重试 3 次缓解偶发不稳定chromeWebSecurityfalse关闭跨域安全限制允许跨域请求配合 API 拦截experimentalMemoryManagementtrue开启实验性内存管理适合跑大量用例的长任务numTestsKeptInMemory1每个用例在内存中保留的 DOM 快照数最小化节省内存viewportWidth/viewportHeight1920/1080统一桌面视口保证断言可复现e2e.baseUrlhttp://localhost:5173本地开发服务器地址Vite 默认端口setupNodeEvents中还做了一件事把浏览器过滤为仅保留 chromium 系且排除 electron即config.browsers.filter((b) b.family chromium b.name ! electron)确保测试始终跑在 Chrome/Edge 等标准 Chromium 浏览器上。2. 测试规格的组织方式一个示例目录一套用例cypress/e2e/ 下按示例名 测试文件的方式组织例如cypress/e2e/base-antd/all.cy.ts —— base-antd 示例的完整 CRUD 流程cypress/e2e/table-antd-advanced/ —— 拆分为categories.cy.ts与posts.cy.ts两个资源auth-*、form-*、inferencer-*、with-nextjs、with-remix-*等 80 个规格文件以 base-antd 的 all.cy.ts 为例它用一个describe覆盖了资源的五个核心动作describe(base-antd, () { beforeEach(() { cy.clearAllCookies(); cy.clearAllLocalStorage(); cy.clearAllSessionStorage(); cy.visit(/); }); it(should list resource, () { cy.resourceList(); }); it(should create resource, () { cy.resourceCreate({ ui: antd }); }); it(should edit resource, () { cy.resourceEdit({ ui: antd }); }); it(should show resource, () { cy.resourceShow(); }); it(should delete resource, () { cy.resourceDelete({ ui: antd }); }); });注意it内部几乎没有选择器全部委托给cy.resourceList()、cy.resourceCreate()这类自定义命令——这正是官方把操作逻辑与测试声明解耦的做法。3. 自定义命令跨 UI 框架的适配层不同 UI 集成antd、chakra-ui、mantine、material-ui的 DOM 结构完全不同官方通过两层命令把差异封装起来第一层语义化资源操作命令cypress/support/commands/resource.ts。它只声明创建资源这个意图内部根据ui参数分发到对应的 UI 适配命令例如const assertNotification (ui: UITypes) { switch (ui) { case antd: return cy.getAntdNotification().should(contain, Success); case chakra-ui: return cy.getChakraUINotification().should(contain, Success); case mantine: return cy.getMantineNotification().should(contain, Success); case material-ui: return cy.getMaterialUINotification().should(contain, Success); } };第二层具体 UI 框架的选择器命令。例如 cypress/support/commands/refine/index.ts 通过 Refine 自带的稳定 class 定位按钮export const getSaveButton () cy.get(.refine-save-button); export const getCreateButton () cy.get(.refine-create-button); export const getDeleteButton () cy.get(.refine-delete-button); export const getEditButton () cy.get(.refine-edit-button); export const getShowButton () cy.get(.refine-show-button); export const getPageHeaderTitle () cy.get(.refine-pageHeader-title);而 cypress/support/commands/antd/index.ts 则封装了 antd 特有的交互如选择下拉、日期选择、Popconfirm 删除确认、表格排序器等export const setAntdSelect ({ id, value }: ISetAntdSelectParams) { return cy .get(#${id}) .click({ force: true }) .get(.ant-select-item[title${value}]) .click({ force: true }) .get(#${id}) .blur(); };所有命令统一在 cypress/support/e2e.ts 中通过Cypress.Commands.add(...)注册并集中调整了超时参数defaultCommandTimeout与requestTimeout均为 20000ms。4. 网络拦截与 fixture不依赖真实后端的 E2E这是官方测试范式中最有价值的部分之一测试完全不依赖真实 API而是通过cy.intercept把api.fake-rest.refine.dev上的请求全部拦下并用本地 fixture 应答。cypress/support/commands/intercepts/api-fake-rest.ts 为posts、categories、blog_posts三个资源定义了 GET/POST/PATCH/DELETE 全套拦截命令。以 GET 单个 post 为例Cypress.Commands.add(interceptGETPost, () { return cy .fixture(posts) .then((posts) { return cy.intercept( { method: GET, hostname: hostname, // api.fake-rest.refine.dev pathname: /posts/*, }, (req) { const id getIdFromURL(req.url); const post posts.find((post) post.id id); if (!post) { req.reply(404, {}); return; } req.reply(post); }, ); }) .as(getPost); });POST/PATCH 则模拟了真实后端行为——把请求体与 fixture 合并并分配新 IDCypress.Commands.add(interceptPOSTPost, () { return cy.fixture(posts).then((posts) cy .intercept( { method: POST, hostname: hostname, pathname: /posts }, (req) { const merged Object.assign({}, req.body, { id: posts.length 1 }); return req.reply(merged); }, ) .as(postPost), ); });这些拦截命令在 cypress/support/e2e.ts 的全局beforeEach中被统一注册同时把 telemetry 请求拦截掉避免污染统计数据beforeEach(() { cy.intercept(https://telemetry.refine.dev/**, { ... }).as(telemetry); cy.interceptGETPosts(); cy.interceptGETPost(); cy.interceptPOSTPost(); cy.interceptPATCHPost(); cy.interceptDELETEPost(); cy.interceptGETBlogPosts(); cy.interceptGETBlogPost(); cy.interceptPOSTBlogPost(); cy.interceptPATCHBlogPost(); cy.interceptDELETEBlogPost(); cy.interceptGETCategories(); cy.interceptGETCategory(); });除了 fake-rest 外cypress/support/commands/intercepts/index.ts 还统一引入了 supabase、strapi-v4、hasura 等数据 provider 的拦截实现让同一套测试代码可以横跨不同后端。测试数据统一存放在 cypress/fixtures/posts.json、categories.json、blog-posts.json、mock-post.json、各认证服务的凭据文件等。例如创建资源时表单填充就使用mock-post.json中的固定数据保证断言结果可预期。从零开始写一个 Refine 应用的端到端测试综合上面拆解的基础设施在自己的 Refine 项目中落地 E2E 测试可以按以下步骤第一步安装并配置 Cypress# 在你的 Refine 项目根目录 npm install -D cypress npx cypress open参照 cypress/cypress.config.ts 配置baseUrl指向本地开发服务器Refine 示例默认 Vite 端口http://localhost:5173并按需设置viewportWidth/Height、retries等参数。第二步建立 fixtures 与网络拦截把接口返回数据写入cypress/fixtures/如posts.json用cy.intercept按资源路径拦截 GET/POST/PATCH/DELETE 请求把.as()命名给拦截别名例如cy.intercept(...).as(getPosts)后续在测试中用cy.wait(getPosts)等待请求完成并读取响应。第三步封装自定义命令隐藏 UI 细节参考 cypress/support/commands/refine/index.ts优先使用 Refine 输出的稳定 classrefine-save-button、refine-create-button等定位按钮如果使用 antd 等 UI 库参考 cypress/support/commands/antd/index.ts 封装下拉、日期、Popconfirm 等专属交互在cypress/support/e2e.ts中用Cypress.Commands.add完成注册。第四步编写资源级测试参考 cypress/support/commands/resource.ts 的create实现一套标准的创建资源流程是export const create ({ ui }: IResourceCreateParams) { cy.getCreateButton().click(); cy.wait(getCategories); cy.location(pathname).should(eq, /posts/create); cy.assertDocumentTitle(Post, create); fillForm(ui); cy.getSaveButton().click(); cy.wait(postPost).then((interception) { const response interception?.response; assertSuccessResponse(response, ui); }); };其中assertSuccessResponse会依次校验HTTP 状态码为 200、响应包含id与category字段、字段值与表单提交内容一致、出现 Success 通知、路由回到/posts。这种拦截别名 响应断言 路由断言 通知断言的组合就是官方推荐的 E2E 断言范式。第五步覆盖文档标题等细节仓库中还提供了文档标题断言命令 cypress/support/commands/document-title-handler.ts通过cy.assertDocumentTitle(Posts, list)使用用于验证 list/create/edit/show 各页面的document.title是否符合 Refine 的标题约定——这也是容易被忽略但很有价值的回归点。运行与维护建议运行方式先启动示例应用例如npm run dev再执行npx cypress run无头模式或npx cypress open交互模式配置中的baseUrl必须与本地开发服务器一致。失败重试E2E 天然存在偶发不稳定官方在cypress.config.ts中设置了retries.runMode: 3建议在 CI 中对重试次数做同样的配置。保持拦截与 fixture 同步每当你调整应用的字段或 API 契约同步更新 cypress/fixtures/ 与拦截命令避免测试通过但数据已过期的假阳性。多框架复用如果你的产品同时提供 antd / MUI / Chakra UI / Mantine 版本可以借鉴resource.ts的ui参数分发模式一套测试逻辑覆盖多个 UI 适配层。自由选择框架官方文档强调 Cypress 只是示例你可以自由选用 Playwright 等任何你熟悉的 E2E 框架但拦截 API fixture 数据 语义化命令 流程断言这套方法论是通用的。总结Refine v5 的测试策略可以浓缩为三句话框架层单元测试交给维护者业务纯逻辑用单元测试锁定用户可见的完整流程用端到端测试守护。仓库中 cypress/ 目录提供了高质量的可参考实现——从全局配置、跨 UI 的命令适配层到不依赖后端的拦截与 fixture 体系再到覆盖 80 个示例规格的测试用例直接对照学习即可在自己的内部工具、管理面板项目中落地同等水平的测试工程。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价