核心功能
写概念文档最怕什么?不是写不出来,而是示例代码“看着对、跑就错”。一旦发布出去,读者复制粘贴报错,评论区直接炸锅。test-writer 的定位很明确:把概念页里的每一段可执行示例,系统性地变成 Vitest 用例,让文档和真实运行结果保持一致。
它的工作方式很像一个严格的“示例审计员”:先扫描页面里的所有代码段,再按类型分组处理。能测的就写断言;故意报错的就用 toThrow;涉及 DOM 的就单独放到 jsdom 环境的测试文件;遇到纯概念图、伪代码、浏览器专属 API 这类不可测片段,会选择跳过并注明原因,避免为了覆盖率硬写假测试。
- 示例提取与分类:把代码分成可测试、DOM 专用、错误示例、概念性片段、浏览器专属等类型,确保后续动作不乱。
- 自动规划测试目录:按概念主题把测试落到既定目录结构里,避免团队里每个人都用自己的习惯乱放。
- 从 console.log 注释反推断言:文档里常见的输出注释,直接转成 expect 断言,读者看到什么,测试就验证什么。
- 按文档章节组织 describe:测试结构贴合文档结构,后面维护时能快速定位“哪段文字对应哪段用例”。
- 带来源行号注释:每组用例都建议写清楚来自文档的行号区间,排查回归问题时特别省命。
实操代码示例
下面是一个“极简但能打”的写法:把文档示例里的输出注释,转成 Vitest 断言。你会发现它不是在教你写花活,而是在帮你把“文档可信度”钉死。
import { describe, it, expect } from 'vitest'
describe('Primitive typeof examples', () => {
it('should return correct type strings', () => {
expect(typeof 'hello').toBe('string')
expect(typeof 42).toBe('number')
})
})
如果示例是“故意报错”的风格,就用 toThrow,把读者会遇到的坑提前锁住:
import { describe, it, expect } from 'vitest'
describe('Intentional error examples', () => {
it('should throw TypeError when accessing property of null', () => {
const obj = null
expect(() => {
obj.property
}).toThrow(TypeError)
})
})
遇到 DOM 示例,建议独立成 .dom.test.js,并声明 jsdom 环境,避免出现 ‘document is not defined’ 这种经典社死:
/**
* @vitest-environment jsdom
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
describe('DOM Event Handlers - click', () => {
let button
beforeEach(() => {
button = document.createElement('button')
button.id = 'myButton'
document.body.appendChild(button)
})
afterEach(() => {
document.body.innerHTML = ''
})
it('should fire click event handler', () => {
const output = []
button.addEventListener('click', (event) => {
output.push('Button clicked!')
output.push(event.type)
})
button.click()
expect(output).toEqual(['Button clicked!', 'click'])
})
})
跑测试也保持简单:全量跑、按概念目录跑、按单文件跑都照顾到,团队协作时特别顺。
npm test
npm test -- tests/fundamentals/primitive-types/
npm test -- tests/fundamentals/primitive-types/primitive-types.test.js
优势分析
同类工具里,有的偏“生成一堆测试模板”,看起来覆盖很高,实际和文档脱节;有的偏“只测业务代码”,对文档示例完全不管。test-writer 的独特卖点是:它盯住的是“概念文档里的代码示例”,把示例当成要交付的产品能力来验收。
- 贴近写作现场:你改了示例、加了段落,就该同步有测试跟上,不然文档就是一次性用品。
- 分类逻辑很实用:DOM、异步、错误示例、不可测片段各就各位,不会把项目测试搞成大杂烩。
- 断言风格统一:明确区分 toBe/toEqual、异步用 resolves/rejects、浮点用 toBeCloseTo,这些细节最容易让新同学翻车。
- 可维护性强:describe 结构跟着文档走,定位问题不需要“全文搜索 + 猜”。
应用场景
你可以把它当成“文档质量的自动化保险”。下面这些场景,基本一上就真香:
- 发布前验收:概念页刚写完,直接生成对应测试,确保每段示例都能运行、输出匹配。
- 重构或升级依赖:比如升级运行时、改 lint 规则、替换 polyfill,示例最容易悄悄坏掉,用测试一跑立刻暴露。
- 多人协作写文档:不同作者写代码风格不同,test-writer 统一落盘结构和命名习惯,减少争论。
- 教学/教程型站点:读者跟着敲代码的场景,容错率极低,示例一错就会被放大,提前用测试挡住。
- 长期维护的知识库:内容越多越难回归,示例测试就是最低成本的持续回归体系。
最佳实践
想让这套流程真正稳住,建议把工程习惯也配齐,不然测试再多也会越跑越慢、越维护越痛。
- 命名别含糊:it 用例统一以 should 开头,且把行为说具体,比如 should resolve promises in order,而不是 works correctly。
- 按章节切 describe:一段文字对应一组用例,后续改文档时只需要动同一块,避免牵一发动全身。
- DOM 清理要强制:afterEach 清空 document.body,并恢复 mocks,防止用例互相污染,出现“单跑过、全跑挂”的诡异情况。
- 异步别偷懒:优先 async/await,Promise 用 resolves/rejects;定时器类逻辑用 fake timers 或测试核心逻辑,避免超时。
- 对不可测片段要有策略:浏览器专属 API 要么 mock,要么明确标注跳过原因,别用假断言糊弄自己。
- 把行号注释当成硬规范:当示例来自 concept.mdx 的某段范围,就写清楚 From lines XX-YY,排查回归会非常快。
当你的概念页越来越多,你会发现“示例测试”其实也是一种资产管理:哪些页面最常坏、哪些章节最常改、哪些示例最脆弱,都能被测试信号直观反映出来。为了更好地管理这些 Skill、复用团队里验证过的工作流,把 test-writer 这类能力统一收纳到 Skill优仓 里,会省下很多重复配置和口口相传的成本,尤其适合需要多人协作、持续更新的知识库项目。









暂无评论内容