新闻详情

新闻详情

首页 / 资讯中心 / 详情

在 Dillinger 中落地可靠测试套件:测试金字塔、AAA 模式与 Mock 策略实战指南

发布时间:2026/9/27 23:39:54来源:尧图网络
在 Dillinger 中落地可靠测试套件:测试金字塔、AAA 模式与 Mock 策略实战指南
前端开发工具【免费下载链接】dillingerThe last Markdown editor, ever.项目地址https://gitcode.com/gh_mirrors/di/dillinger点击查看免费下载本文以 DillingerNext.js React TypeScript 实现的 Markdown 编辑器仓库中的真实测试工程为依托系统讲解测试金字塔、AAA 模式、测试类型选择、单元/集成测试原则、Mock 策略、测试组织、测试数据与反模式等软件测试核心方法论并逐一对照仓库中的vitest单元测试、路由集成测试与playwrightE2E 测试给出可复制的落地示例。读完本文你将掌握一套既有方法论骨架、又有源码级实证的测试编写规范并可直接套用到任意前端项目中。1. 测试金字塔让测试套件拥有正确的层级配比测试金字塔是构建测试策略的起点。它的核心思想是底层单元测试数量最多、速度最快、执行成本最低越往上层测试越少、越慢、越接近真实用户路径。/\ E2E (Few) / \ Critical flows /----\ / \ Integration (Some) /--------\ API, DB queries / \ /------------\ Unit (Many) Functions, classes单元测试Many覆盖函数、类等最小逻辑单元数量最多执行毫秒级完成。集成测试Some覆盖 API、数据库查询等跨模块协作路径数量适中。E2E 测试Few只覆盖关键用户流程数量最少因为其运行最慢、最脆弱。Dillinger 仓库正是按这一金字塔组织的tests/lib/、tests/store/、tests/hooks/、tests/components/下是大量单元测试tests/routes/下是对 Next.js API Route 的集成测试tests/e2e/下是少量 Playwright 端到端测试如 editor.spec.ts。从目录布局即可看出多而快的测试被放在金字塔底部这正是金字塔原则在真实项目中的直接体现。2. AAA 模式每个测试的固定三段式结构AAAArrange–Act–Assert是编写清晰测试的统一范式StepPurposeArrange准备测试数据Set up test dataAct执行被测代码Execute code under testAssert验证结果Verify outcome在 store.test.ts 中可以逐行看到 AAA 的完整落地。以hydrate的用例为例it(restores documents from localStorage, () { // Arrange写入 localStorage 作为测试数据 const doc createTestDocument({ id: stored-1, title: Stored.md }); localStorage.setItem(files, JSON.stringify([doc])); localStorage.setItem(currentDocument, JSON.stringify(doc)); // Act执行被测动作 useStore.getState().hydrate(); // Assert校验副作用结果 const state useStore.getState(); expect(state.documents).toHaveLength(1); expect(state.documents[0]?.id).toBe(stored-1); expect(state.currentDocument?.id).toBe(stored-1); });再如 export-markdown.route.test.ts 中对POST /api/export/markdown的断言it(returns 400 when markdown body is missing, async () { // Arrange const response await exportMarkdown(buildRequest({ title: no-body.md })); // Act发生在 await 中之后直接 Assert expect(response.status).toBe(400); expect(json.error).toBe(Markdown content is required); });坚持 AAA 三段式能让测试的意图一目了然任何读者都能快速定位数据从哪来、测了什么、期望什么。3. 测试类型选择何时用哪种测试TypeBest ForSpeedUnitPure functions, logicFast参考阈值 50msIntegrationAPI, DB, servicesMediumE2ECritical user flowsSlowDillinger 的工程配置与之一一对应单元测试由 Vitest 驱动配置见 vitest.config.tsenvironment: jsdominclude: [tests/**/*.test.ts, tests/**/*.test.tsx]。典型如 markdown.test.ts 中renderMarkdown的纯函数渲染断言、store.test.ts 中 Zustand store 的状态迁移断言。集成测试同样基于 Vitest但通过文件头注释// vitest-environment node切换到 Node 环境直接调用路由处理函数并断言 HTTP 状态码与响应头见 export-markdown.route.test.ts。E2E 测试由 Playwright 驱动配置见 playwright.config.tstestDir: ./tests/e2e通过webServer自动启动npx next dev -H 127.0.0.1 -p 3005供测试访问配置注释说明因生产构建存在既有预渲染错误E2E 使用 dev 模式。选择原则优先用单元测试覆盖逻辑跨模块交互交给集成测试只有核心用户流程新建、切换、删除文档、Zen 模式、设置持久化等才动用 E2E见 editor.spec.ts。4. 单元测试原则好的单元测试应具备的五个属性PrincipleMeaningFast参考阈值 100ms eachIsolated无外部依赖No external depsRepeatable结果始终一致Same result alwaysSelf-checking无需人工验证No manual verificationTimely与代码同步编写Written with codeDillinger 的单元测试通过两层机制保障这五个属性隔离环境vitest.setup.ts 为每个测试文件注入localStorage、matchMedia与ResizeObserver的内存实现使组件与 store 测试完全不依赖真实浏览器 APIvitest.config.ts开启restoreMocks: true与clearMocks: true保证 mock 状态在用例之间自动复位。确定性输入markdown.test.ts 使用固定 Markdown 字符串断言固定的 HTML 输出例如**bold text**断言输出strongbold text/strong并专门用一组一致性用例验证renderMarkdown对同一输入多次调用返回相同结果——这正是 Repeatable 原则的测试化表达。什么该测、什么不该测TestDont TestBusiness logicFramework codeEdge casesThird-party libsError handlingSimple getters仓库中的边界用例很有参考价值store.test.ts专门验证localStorage 中 JSON 损坏时hydrate不崩溃、状态保持不变捕获SyntaxError并打印Failed to hydrate state:useGitHub.test.tstests/hooks/useGitHub.test.ts则系统覆盖网络错误、非 2xx 响应时各 API 方法优雅降级——这些都是典型的边界与错误处理应当测试的场景。5. 集成测试原则测什么AreaFocusAPI endpointsRequest/responseDatabaseQueries, transactionsExternal servicesContractsDillinger 的集成测试聚焦 API endpointstests/routes/ 下的用例直接构造Request对象调用路由的POST处理器断言状态码、Content-Disposition文件名如Draft.md、My_Notes.md、Content-Type: text/markdown; charsetutf-8、空正文返回 400、非法 JSON 返回 500 等契约行为。路由测试以请求/响应契约为核心不依赖真实浏览器。设置与清理PhaseActionBefore All连接资源Connect resourcesBefore Each重置状态Reset stateAfter Each清理Clean upAfter All断开连接Disconnect对应实现单元/组件级vitest.setup.ts 通过afterEach统一执行cleanup()卸载 Testing Library 渲染的 DOM并localStorage.clear()store.test.ts与settings-modal.test.tsxtests/components/settings-modal.test.tsx在beforeEach中调用resetStore()把 Zustand store 恢复到初始快照。E2E 级playwright.config.ts 配置trace: on-first-retry、screenshot: only-on-failure、video: retain-on-failure失败即自动留痕便于排查测试内通过page.addInitScript在页面加载前注入localStorage种子数据实现可重复、无脏状态的用例隔离。6. Mock 原则何时 MockMockDont Mock外部 APIExternal APIs被测代码本身The code under test数据库单元测试中简单依赖Simple dependencies时间/随机数Time/random纯函数Pure functions网络Network内存存储In-memory storesMock 类型TypeUseStub返回固定值Return fixed valuesSpy记录调用Track callsMock设置预期Set expectationsFake简化实现Simplified implementationDillinger 的测试提供了教科书级的 Mock 范例Stub Spy 结合useGitHub.test.ts 在beforeEach中用vi.stubGlobal(fetch, fetchMock)替换全局 fetch并让fetchMock vi.fn(() mockFetchResponse({ connected: false, user: null }))——既能按 URL 返回固定响应Stub又能事后断言fetchMock被以哪些参数调用Spy例如expect(fetchMock).toHaveBeenCalledWith(/api/github/status)。Fakevitest.setup.ts 用Map实现了一个完整的localStorageFake并 stub 掉matchMedia与ResizeObserver使 jsdom 环境下组件测试可运行。按 URL 分流的响应编排useGitHub.test.ts中大量使用fetchMock.mockImplementation((url) ...)按/api/github/status、/api/github/repos?owner...、/api/github/branches?...、/api/github/files?...分别返回不同数据模拟一个完整的仓库浏览状态机。编辑器实例的 Mockstore.test.ts 用vi.fn()伪造Monaco.editor.IStandaloneCodeEditorgetSelection、executeEdits、focus从而在不启动 Monaco 的情况下验证insertMarkdownAtCursor会以dillinger-inline-insert为资源标识调用executeEdits。关键边界只 Mock 外部依赖网络、全局 API、重型第三方绝不 Mock 被测代码自身。上述测试全部围绕真实组件、真实 hook、真实 store 逻辑展开Mock 的只是它们的环境依赖。7. 测试组织命名与分组命名模式PatternExampleShould behaviorshould return error when...When conditionwhen user not found...Given-when-thengiven X, when Y, then Z分组结构LevelUsedescribe分组相关测试Group related testsit/test单个用例Individual casebeforeEach公共初始化Common setupDillinger 的测试命名可以归纳为三种风格均满足描述性命名即文档的要求行为驱动式如hydrates a default document when storage is empty、creates a new imported document without overwriting the current onestore.test.ts。能力边界式如fetchBranches handles network error gracefully、fetchFileContent returns null for non-ok API responseuseGitHub.test.ts。功能断言式如renders checked checkboxes、applies bootstrap table classes to tablesmarkdown.test.ts。分组上store.test.ts 用嵌套describe(createDocument / hydrate / persist / deleteDocument ...)把同一 action 的多个用例聚在一起E2E 侧 editor.spec.ts 则用test.describe(Document creation / Zen mode / Settings modal ...)按用户流程组织beforeEach中统一执行种子数据注入。8. 测试数据策略三种数据生成方式ApproachUseFactories生成测试数据Generate test dataFixtures预定义数据集Predefined datasetsBuilders流式对象创建Fluent object creationDillinger 仓库同时使用了 Factory 与 Fixture 两种策略可作为参考样板Factory工厂函数store.test.ts 定义createTestDocument(overrides)以默认值 覆盖项的方式快速生成文档对象function createTestDocument(overrides: Partial{ id: string; title: string; body: string; createdAt: string; } {}) { return { id: overrides.id ?? doc-1, title: overrides.title ?? Test.md, body: overrides.body ?? # Test, createdAt: overrides.createdAt ?? 2026-03-10T00:00:00.000Z, }; }Fixture固定数据集editor.spec.ts 定义seededDocument、secondDocument、defaultProfile作为 E2E 种子并通过seedSingleDocument(page)/seedMultipleDocuments(page)封装page.addInitScript在页面加载前写入files、currentDocument、profileV3三个 localStorage 键。数据原则使用贴近真实的数据如文档标题带.md后缀、body 为真实 Markdown对非关键字段随机化仓库中测试因追求确定性而使用固定值随机化工具如 faker 可按需引入共享公共 fixturesdefaultProfile在多文件间复用保持数据最小化只放被测路径需要的字段9. 最佳实践清单PracticeWhy每个测试一个核心断言One assert per test失败原因清晰Clear failure reason测试相互独立Independent tests无顺序依赖No order dependency测试保持快速Fast tests可以频繁运行Run frequently描述性命名Descriptive names自文档化Self-documenting及时清理Clean up避免副作用Avoid side effects这些实践在 Dillinger 工程中已被制度化独立性store.test.ts的resetStore()、beforeEach中的localStorage.clear()加上vitest.config.ts的restoreMocks/clearMocks共同保证用例之间零状态泄漏。快速单元与组件测试在 jsdom 中毫秒级完成package.json把测试拆分为test:unitvitest run与test:e2e日常开发可只跑前者避免被 E2E 拖慢节奏。清理vitest.setup.ts的afterEach统一执行 Testing Librarycleanup()与存储清空组件测试无需各自手动卸载。可观测Playwright 失败自动产出 trace、截图与视频见 playwright.config.ts把排查成本压到最低。10. 反模式这些做法必须避免❌ Dont✅ Do测试实现细节Test implementation测试行为Test behavior重复测试代码Duplicate test code使用工厂Use factories复杂测试搭建Complex test setup简化或拆分Simplify or split忽略偶发失败Ignore flaky tests修复根因Fix root cause跳过清理Skip cleanup重置状态Reset state仓库中的正面示例恰是这些反模式的镜像测行为而非实现markdown.test.ts 只断言渲染产出的 HTML 结构与内容从不断言 markdown-it 的内部调用settings-modal.test.tsx 通过getByRole(switch, ...)与aria-checked断言可访问的行为而非组件内部 state 变量。用工厂消灭重复createTestDocument、seedSingleDocument正是为消除每个用例手写 localStorage 种子的重复而存在。直面失败而非忽略useGitHub.test.ts对网络错误、非 2xx 响应逐一断言优雅降级结果而不是try/catch吞掉异常Playwright 的trace: on-first-retry则是为偶发失败保留诊断证据、定位根因。主动重置状态从beforeEach的resetStore()到afterEach的cleanup()再到 vitest 配置级的 mock 复位形成三层清理防线。11. 在 Dillinger 仓库中运行与验证仓库在 package.json 中提供了完整的测试命令矩阵npm run test:unit # vitest run —— 跑单元 路由集成测试 npm run test:watch # vitest —— 监听模式开发时持续反馈 npm run test:e2e # npm run build playwright test —— 构建后跑 E2E npm run test:e2e:headed # 以有头浏览器模式运行 E2E便于观察 npm run test # 依次执行 test:unit 与 test:e2e npm run verify # lint typecheck 单元 E2E 全量门禁其中verify把 lintnext lint、typechecktsc --noEmit、单元测试与 E2E 串成一条发布前门禁start:testnext start -H 127.0.0.1 -p 3005则为手动联调提供与 E2E 一致的端口。E2E 依赖的浏览器与服务器由 playwright.config.ts 管理webServer自动拉起npx next dev -H 127.0.0.1 -p 3005reuseExistingServer: !process.env.CI允许本地复用已启动的实例。12. 测试即文档Remember:Tests are documentation. If someone cant understand what the code does from the tests, rewrite them.测试就是文档。如果别人无法通过测试理解代码在做什么就重写它们。这条原则在 Dillinger 仓库中得到了彻底执行markdown.test.ts 完整记录了renderMarkdown支持的全部 Markdown 能力标题、粗斜体、高亮、脚注、上下标、删除线插入、定义列表、缩写、目录、KaTeX 数学公式、行号属性、表格类名、标题锚点useGitHub.test.ts 完整刻画了 GitHub 集成的状态机status → orgs → repos → branches → files → file content → save → unlinkstore.test.ts 则穷举了 store 的 hydrate、persist、CRUD 与异常分支。任何新成员只需读一遍测试就能在不看实现的情况下掌握系统行为契约——这正是测试作为文档的最高价值形态。延伸阅读测试配置详见 vitest.config.ts 与 playwright.config.ts全局测试环境与 mock 见 vitest.setup.ts单元测试样本见 tests/lib/、tests/store/、tests/hooks/、tests/components/路由集成测试见 tests/routes/端到端测试见 tests/e2e/。赞分享前端开发工具【免费下载链接】dillingerThe last Markdown editor, ever.项目地址https://gitcode.com/gh_mirrors/di/dillinger点击查看免费下载相关推荐ag-kit 测试模式实践指南从测试金字塔到 Mock 策略的完整落地方案ag kit 测试模式实践指南从测试金字塔到 Mock 策略的完整落地方案 本文以 ag kit 仓库中 testing patterns 技能文档 http人工智能AI 技能GE图引擎SetDataType API文档SetDataTypea nameZH CN_TOPIC_0000002519215175 /a 产品支持情况a namesection7891人工智能深度学习模型编译模型优化编译器AscendDillinger 项目测试工程师指南从测试金字塔到 Playwright E2E 的完整落地实践Dillinger 项目测试工程师指南从测试金字塔到 Playwright E2E 的完整落地实践 本篇技术指南以 Dillinger 开源仓库GitHub前端开发工具上一篇Roc 名义类型模块中关联嵌套类型的前向引用从快照测试看 ModType.InternalType 的解析与校验下一篇jql革命性JSON查询工具用Lispy语法轻松处理复杂数据创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

PyTorch CNN入门:mnist.py手写数字识别实战与避坑指南 2026/9/28 1:19:29

PyTorch CNN入门:mnist.py手写数字识别实战与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Python + BrainFlow无线采集OpenBCI Cyton脑电数据完整指南 2026/9/28 1:19:29

Python + BrainFlow无线采集OpenBCI Cyton脑电数据完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
中兴K10刷机底层原理:从AVB签名到A/B分区实战解析 2026/9/28 1:19:28

中兴K10刷机底层原理:从AVB签名到A/B分区实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
RK3568设备树实战:从DTS修改到外设驱动全链路解析 2026/9/28 1:19:22

RK3568设备树实战:从DTS修改到外设驱动全链路解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
STM32学习中的“不贪”与“不放”:从入门到精通的可持续进阶路径 2026/9/28 1:19:22

STM32学习中的“不贪”与“不放”:从入门到精通的可持续进阶路径

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
macOS固件调试串口日志工具CoolTerm配置与日志管理实战 2026/9/28 1:19:22

macOS固件调试串口日志工具CoolTerm配置与日志管理实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉