首页/新闻资讯/正文详情

RedwoodJS 第一个组件测试实战:从失败用例到 Cell Mock 与摘要渲染测试

发布时间:2026/9/23 17:52:55 来源:云帆数科 栏目:资讯中心
RedwoodJS 第一个组件测试实战:从失败用例到 Cell Mock 与摘要渲染测试
后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载本文是 RedwoodJS 官方教程「构建博客」第五章的核心环节。当你用 Storybook 完成了组件的第一阶段创建/更新之后第二阶段就是用测试确认功能符合预期。本文以给博客文章新增summary摘要功能为线索完整演示如何运行yarn rw test交互式测试、为什么已有测试会失败、如何用getByText/queryByText重构测试断言、Cell Mock 的底层机制以及最终如何为Article组件的摘要渲染编写独立测试。读完本文你将掌握 RedwoodJS 测试驱动组件开发的标准流程以及redwoodjs/testing在 Web 端提供的完整测试 API。为什么 Storybook 之后还要写测试在 RedwoodJS 的组件开发工作流里Storybook 是「创建/更新组件的第一阶段」——它让你在隔离环境中看到组件在不同状态下的样子而「第二阶段」则是用自动化测试确认组件的功能行为。两者互补Storybook 回答「组件长什么样」测试回答「组件行为是否符合预期」。如果你此前从未写过任何形式的测试RedwoodJS 官方提供了完整的 测试总览文档其中不仅包含测试哲学甚至手把手教你用纯 JavaScript 从零构建一个超级简单的测试运行器以揭开测试框架的神秘面纱。本文则聚焦于 Web 端组件测试的实战环节。运行测试yarn rw test与交互式 watch 模式如果你在上一节Storybook结束时测试进程仍在运行直接按下a键即可运行all全部测试。如果进程已经停止重新启动yarn rw testRedwoodJS 的测试运行器基于 Jest 的交互式 watch 模式。几个常用快捷键a运行全部测试o回到「只测试自上次提交以来有变更的文件」模式两者之间可以随意切换只跑变更文件更快跑全量测试更稳妥二者没有对错之分。当你进入这个 watch 模式后一个问题随之而来——猜猜什么测试会失败第一次看到测试失败全文匹配 vs 摘要渲染在上一个章节中我们在ArticlesCell里让Article只显示文章的summary摘要而非全文。而ArticlesCell的既有测试此前匹配的是博客文章的完整正文test(Success renders successfully, async () { const articles standard().articles render(Success articles{articles} /) expect(screen.getByText(articles[0].title)).toBeInTheDocument() expect( screen.getByText( Neutra tacos hot chicken prism raw denim, put a bird on it enamel pin post-ironic vape cred DIY. Str... ) ).toBeInTheDocument() })由于页面上现在只有摘要测试所寻找的完整正文已经不存在于 DOM 中因此该断言失败。这正是测试的价值它敏锐地捕捉到了「页面内容被改变」这一事实。接下来我们要做的不是删掉测试而是让测试去验证新的预期行为。关于「如何写出最好的测试」有整本书的篇幅在讨论任何选择都会有人指摘。最简单的方案是把组件实际输出复制进测试当作期望文本——但这样测试就对Article的内部实现比如硬编码的截断字符数知道得太多了。一个被广泛接受的测试理论是被测对象应当是黑盒——你无法看到它的内部只能验证「输入某些数据后输出什么数据」。因此与其精确匹配截断后的完整字符串不如基于summary这个 prop 的存在对行为做三个合理假设文章正文的完整内容不应该出现但正文的前几个词至少应该出现显示的文本应以...结尾。这样即使将来把截断长度从 10 个字符调整为 25 个词甚至几百个词测试依然稳固。它没有覆盖到的边界情况是当正文本身短于截断长度时全文会完整显示此时truncate()函数也不应追加...——这个功能及其测试就留给你在空闲时间自行补全了。更新 ArticlesCell 测试完整的逐项断言下面是用上述「三项合理假设」重写后的完整测试文件import { render, screen, within } from redwoodjs/testing import { Loading, Empty, Failure, Success } from ./ArticlesCell import { standard } from ./ArticlesCell.mock describe(ArticlesCell, () { test(Loading renders successfully, () { expect(() { render(Loading /) }).not.toThrow() }) test(Empty renders successfully, async () { expect(() { render(Empty /) }).not.toThrow() }) test(Failure renders successfully, async () { expect(() { render(Failure error{new Error(Oh no)} /) }).not.toThrow() }) test(Success renders successfully, async () { const articles standard().articles render(Success articles{articles} /) articles.forEach((article) { const truncatedBody article.body.substring(0, 10) const matchedBody screen.getByText(truncatedBody, { exact: false }) const ellipsis within(matchedBody).getByText(..., { exact: false }) expect(screen.getByText(article.title)).toBeInTheDocument() expect(screen.queryByText(article.body)).not.toBeInTheDocument() expect(matchedBody).toBeInTheDocument() expect(ellipsis).toBeInTheDocument() }) }) })TypeScript 版本完全一致仅扩展名不同ArticlesCell.test.tsx。注意第 81 行新增了within的导入import { render, screen, within } from redwoodjs/testing。这段测试遍历standard()mock 中的每篇文章逐条断言const truncatedBody article.body.substring(0, 10)创建变量truncatedBody取正文的前 10 个字符。const matchedBody screen.getByText(truncatedBody, { exact: false })在渲染出的 HTML 中查找包含截断文本的元素。注意{ exact: false }默认情况下getByText要求文本精确且唯一地匹配但这里元素里大概率不止这 10 个字符所以关闭精确匹配。const ellipsis within(matchedBody).getByText(..., { exact: false })在上一行找到的元素内部继续查找...同样使用非精确匹配。expect(screen.getByText(article.title)).toBeInTheDocument()确认文章的标题出现在页面中。expect(screen.queryByText(article.body)).not.toBeInTheDocument()当尝试查找完整正文时它不应该存在。expect(matchedBody).toBeInTheDocument()断言截断后的文本确实存在于文档中。expect(ellipsis).toBeInTheDocument()断言省略号确实存在。getByText 与 queryByText 的区别这是理解这段测试的关键知识点getByText()如果文本在文档中找不到直接抛错测试立即失败queryByText()找不到时返回null让你继续执行后续断言——这正是「断言某段文本不存在于页面」的惯用方式。这两个 API 来自 DOM Testing Library 的 Query 体系RedwoodJS 通过redwoodjs/testing将它们完整转发出来见下文「redwoodjs/testing从何而来」。反证测试确实在验证摘要功能保存测试文件后watch 模式会自动重跑并通过。若想确认这个测试真的在验证摘要功能而非自欺欺人可以打开ArticlesCell.js删除summary{true}这个 prop或改为false——此时页面会渲染完整正文expect(screen.queryByText(article.body)).not.toBeInTheDocument()这一条断言就会因为全文出现在文档中而失败。验证完毕后记得把summary{true}改回去。Mocks 到底是什么Cell 旁边的.mock文件你可能已经好奇测试里的文章数据从哪来开发数据库不——它们来自Mock。它就是与组件、测试、stories 文件并排的那个ArticlesCell.mock.js。在 Cell 中组件顶部定义的QUERY会发起 GraphQL 请求返回的数据交给Success组件渲染。但我们显然不想为了 Storybook 或测试去启动 api-side 服务、准备真实数据库数据。RedwoodJS 的做法是拦截这些 GraphQL 调用直接返回 mock 中定义的数据。这个机制在源码里有清晰的实现。在 packages/testing/src/web/mockRequests.ts 中RedwoodJS 基于 MSWMock Service Worker注册了mockGraphQLQuery与mockGraphQLMutation两个核心 API在 JestNodeJS 环境下使用setupServer在 Storybook浏览器环境下使用setupWorker请求处理器支持惰性注册在服务器启动前调用mockGraphQLQuery会把 handler 存入队列服务器启动后一次性排空并注册数据函数DataFunction可以接收(variables, { req, ctx })从而根据请求变量动态返回数据并支持once、networkError等响应增强器。而 mock 文件本身的位置由 packages/testing/src/web/findCellMocks.ts 决定——它用 fast-glob 查找**/*Cell/*.mock.{js,ts,jsx,tsx}模式的文件即mock 只存在于 Cell 旁边的同名目录中。对应的单元测试 packages/testing/src/web/tests/findCellMocks.test.ts 验证了该行为它从example-todo-mainfixture 中精确找到了NumTodosCell.mock.js、NumTodosTwoCell.mock.js、TodoListCell.mock.js三个文件。一个真实可参考的 mock 实例是 TodoListCell.mock.js。认识standard()mock 的命名导出你给 mock 起的名字会直接出现在测试与 stories 文件中——只要按需 import 即可生成器生成的测试文件默认帮你导入了standard。例如export const standard () ({ articles: [ { id: 1, title: First Post, body: Neutra tacos hot chicken prism raw denim..., createdAt: 2020-01-01T12:34:56Z, }, { id: 2, title: Second Post, body: Master cleanse gentrify irony put a bird on it..., createdAt: 2020-01-01T12:34:56Z, }, ], })返回对象中的第一个 key 是articles——它恰好就是 Cell 中Success组件所期望接收的 prop 名export const Success ({ articles }) { return ( { articles.map((article) Article article{article} /) } ) }TypeScript 版本使用CellSuccessPropsArticlesQuery类型标注。因此在 story 或测试中你可以直接把standard()的结果展开传给Success组件import { Success } from ./ArticlesCell import { standard } from ./ArticlesCell.mock export const success () { return Success ? Success {...standard()} / : null } export default { title: Cells/ArticlesCell }如果觉得展开语法太隐晦也可以像真实代码那样显式传属性import { Success } from ./ArticlesCell import { standard } from ./ArticlesCell.mock export const success () { return Success ? Success articles{standard().articles} / : null } export default { title: Cells/ArticlesCell }你可以定义任意多个 mock如standard、empty、withError等按需 import 名字、作为 props 传给组件即可。redwoodjs/testing 从何而来测试代码第一行import { render, screen, within } from redwoodjs/testing中的 API 并非凭空出现。查看 packages/testing/src/web/index.ts 可以看到RedwoodJS 的 Web 测试入口做了三件事引入./global在全局声明mockGraphQLQuery、mockGraphQLMutation类型见 global.tsexport * from testing-library/react将render、screen、within、waitFor等 Testing Library API 完整转发用自定义的 customRender 覆盖默认render——它把组件包进MockProviders内置 MockRouter、MockParamsProvider 等确保 Cell、路由参数、认证状态在测试中都能正常工作。这解释了为什么你能在测试中直接使用screen.getByText、within以及为什么 Cell 测试能免去手动配置 MSW 的繁琐步骤。测试 Article 组件测试「公开」的渲染结果测试套件重新全绿了但这里有个陷阱我们从未给Article组件的summary功能本身写过测试。ArticlesCell的测试只验证了「渲染出来的文章包含摘要」而「摘要到底是什么样」是只有Article才掌握的知识。在开发节奏加快时这类测试盲区极易被遗漏。TDD测试驱动开发正是为了对抗这种倾向先写测试、看它失败、再写实现代码让它通过确保每一行真实代码都有测试背书。本文采用的方式则被戏称为「开发驱动测试」Development Driven Testing——先写功能再补测试。你最终会在这两种风格之间找到平衡但有一条金科玉律永远成立有测试总比没有测试强。Article的摘要功能很简单但测试它有两种思路把truncate()函数导出直接单测它测试组件的最终渲染结果。本文选择后者。理由很务实truncate()目前「属于」Article外部世界不需要关心它的存在。只有当未来另一个组件也需要截断文本时才值得把它抽到共享位置、各自 import 并为其编写独立测试。眼下保持关注点分离只测试这个组件「公开」的东西——渲染结果。由于「摘要截多长」的知识就封装在Article内部此时让测试与组件渲染结果紧耦合是可以接受的ArticlesCell不需要知道截断长度只需要知道「有东西在缩短文本」。你当然可以不断重构代码让测试对代码变更绝对免疫但你真的需要那种级别的灵活性吗测试永远是一种权衡。下面是完整的Article测试文件。我们把示例文章数据提取为常量ARTICLE同时用于两个用例一个验证不传summaryprop 时渲染全文一个验证传summary{true}时渲染摘要import { render, screen } from redwoodjs/testing import Article from ./Article const ARTICLE { id: 1, title: First post, body: Neutra tacos hot chicken prism raw denim, put a bird on it enamel pin post-ironic vape cred DIY. Street art next level umami squid. Hammock hexagon glossier 8-bit banjo. Neutra la croix mixtape echo park four loko semiotics kitsch forage chambray. Semiotics salvia selfies jianbing hella shaman. Letterpress helvetica vaporware cronut, shaman butcher YOLO poke fixie hoodie gentrify woke heirloom., createdAt: new Date().toISOString(), } describe(Article, () { it(renders a blog post, () { render(Article article{ARTICLE} /) expect(screen.getByText(ARTICLE.title)).toBeInTheDocument() expect(screen.getByText(ARTICLE.body)).toBeInTheDocument() }) it(renders a summary of a blog post, () { render(Article article{ARTICLE} summary{true} /) expect(screen.getByText(ARTICLE.title)).toBeInTheDocument() expect( screen.getByText( Neutra tacos hot chicken prism raw denim, put a bird on it enamel pin post-ironic vape cred DIY. Str... ) ).toBeInTheDocument() }) })TypeScript 版本Article.test.tsx内容完全一致。保存文件后watch 模式自动重跑测试套件依然全绿。最后一件小事显式传入summary{false}还记得我们在Article中把summaryprop 默认设为false吗第一个测试用例完全不传summaryprop已经覆盖了这个默认行为。但我们还没有测试显式传入summary{false}的情况。如果你追求 100% 代码覆盖率可以现在就补上这个用例。这也是一个很好的练习用screen.queryByText(ARTICLE.body)断言显式关闭摘要时全文渲染、且不出现...。小结回顾这一章我们完成了 RedwoodJS 组件测试的完整闭环测试先行于功能变更时必然失败——ArticlesCell由全文改摘要旧断言立刻报警用黑盒假设重构断言——通过exact: false匹配截断文本、用within缩小搜索范围、用queryByTextnot.toBeInTheDocument()断言「不存在」理解 Cell Mock 机制——standard()等命名导出与Success的 props 一一对应GraphQL 请求被 MSW 拦截底层实现在 mockRequests.tsmock 文件被 findCellMocks.ts 自动发现为组件公开行为补测试——Article的summary功能通过渲染结果而非内部函数来验证。关于「服务器被 mock 了api-side 代码怎么测」的问题将在下一章创建全新博客功能时揭晓——那将进入 RedwoodJS 测试的另一半Service 与 Scenario 测试。赞分享后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载相关推荐RedwoodJS 实战编写你的第一个组件测试——Cell 渲染、Mock 数据与黑盒测试方法论RedwoodJS 实战编写你的第一个组件测试——Cell 渲染、Mock 数据与黑盒测试方法论 本文基于 RedwoodJS 官方教程 Chapter 5《后端前端Web框架开发工具Redwood 项目实战为 Cell 编写第一个测试——从失败用例到 Mock 数据驱动的组件测试Redwood 项目实战为 Cell 编写第一个测试——从失败用例到 Mock 数据驱动的组件测试 导读 本篇技术指南源自 Redwood 官方教程第五章《后端前端Web框架开发工具RedwoodJS 单元测试实战从失败的 Cell 测试到 Article 摘要功能的完整测试链RedwoodJS 单元测试实战从失败的 Cell 测试到 Article 摘要功能的完整测试链 本篇技术指南围绕 RedwoodJS 官方教程第五章节的 后端前端Web框架开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

光伏板数据集标注与YOLOv8训练:从VOC格式到模型部署全流程
光伏板数据集标注与YOLOv8训练:从VOC格式到模型部署全流程

简介:光伏板数据集是一份面向目标检测与光伏巡检场景的标注数据资源,由LabelImg手工绘制边界框并生成对应XML标注文件,适合希望直接开展YOLOv8训练和算法验证的研究者或开发者。资源包共377个文件,包含137张PNG图片、120张JPG图片… · 2026/9/23 17:52:55

Monero depends 依赖构建系统:编写包配方(recipe)的完整指南
Monero depends 依赖构建系统:编写包配方(recipe)的完整指南

区块链金融科技 【免费下载链接】monero Monero: the secure, private, untraceable cryptocurrency 项目地址: https://gitcode.com/gh_mirrors/mo/monero 点击查看 免费下载 导读 Monero 仓库中的 contrib/depends 是一套用于跨平台构建并缓存第三方依赖的独立构… · 2026/9/23 17:52:49

适合新手临摹的彩铅画源码深度剖析
适合新手临摹的彩铅画源码深度剖析

新手临摹彩铅画渲染慢一文搞懂性能优化实战 报错一堆看不懂 StackTrace?别急着删库跑路。 刚跑通“适合新手临摹的彩铅画”渲染引擎,界面卡得像 PPT,日志里全是 OutOfMemoryError 和 GC overhead… · 2026/9/23 17:52:49

PX4 计算机视觉技术指南:光学流、动作捕捉、视觉惯性里程计与碰撞预防
PX4 计算机视觉技术指南:光学流、动作捕捉、视觉惯性里程计与碰撞预防

嵌入式物联网机器人自动驾驶智能硬件 【免费下载链接】PX4-Autopilot PX4 Autopilot Software 项目地址: https://gitcode.com/gh_mirrors/px/PX4-Autopilot 点击查看 免费下载 PX4 通过运行在伴机电脑上的计算机视觉系统,为飞控提供两大核心能力&#… · 2026/9/23 18:35:38

WPS与Word中插入手写电子签名:透明背景PNG制作与批量处理技巧
WPS与Word中插入手写电子签名:透明背景PNG制作与批量处理技巧

1. 手写签名电子化的真实需求场景合同、协议、审批单、报价单,这些文件在最终定稿之前,几乎都逃不过一个环节——签字。传统做法是把文件打印出来,手写签完,再扫描回传。一次两次还行,文件一多,打印、签字、… · 2026/9/23 18:35:38

淘宝付款页面打不开?3个高频坑点与避坑指南
淘宝付款页面打不开?3个高频坑点与避坑指南

淘宝付款页面打不开?3个高频坑点与避坑指南 配置环境就卡半天,淘宝付款页面打不开,这种“灵异”现象在测试和开发环境里太常见了。别急着甩锅给网络,90%的情况是前端路由拦截或后端接口鉴权出了问题。这份避坑指南,直接帮你定位根因。… · 2026/9/23 18:35:38

从‘假莫妮卡‘拆解真假身份叙事:四幕结构、角色设计与冲突节奏
从‘假莫妮卡‘拆解真假身份叙事:四幕结构、角色设计与冲突节奏

1. 从一句标题里能读出什么:拆解"假莫妮卡"这个经典叙事装置第一次看到"first season twenty-first episode, the fake Monica!!!"这个标题,我脑子里蹦出来的不是某一部具体的剧,而是一整套被反复验证过的叙事套路——&q… · 2026/9/23 18:35:38

BIOS、MBR与引导扇区:开机黑盒的底层启动链解析
BIOS、MBR与引导扇区:开机黑盒的底层启动链解析

1. 开机那几秒,到底发生了什么?你有没有过这种经历:按下电源键,屏幕一黑,几秒后突然跳出“Press F2 to enter Setup”或者“Boot Device Not Found”?又或者装系统时卡在“Loading operating system…”不动… · 2026/9/23 18:35:25

1111111数字序列:工程师必须警惕的七位隐形故障源
1111111数字序列:工程师必须警惕的七位隐形故障源

1. 什么是“1111111”——一个被低估的数字序列现象“1111111”不是一串随意敲出的键盘残留,也不是某个密码学竞赛里的冷门题干。它是一组在真实系统中反复触发异常行为的七位同构数字序列,我在过去三年处理过的27个生产环境故障里,有5个直接… · 2026/9/23 18:35:25

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码