33-js-concepts 项目如何用 Vitest 为文档代码示例编写测试并运行
【免费下载链接】33-js-concepts📜 33 JavaScript concepts every developer should know.项目地址: https://gitcode.com/GitHub_Trending/33/33-js-concepts
33-js-concepts 项目把 33 个 JavaScript 概念文档里的代码示例当成可执行代码来对待:每个概念文档中的console.log示例,都要在tests/目录里有一份对应的断言测试来验证其行为。本文基于项目的 CONTRIBUTING.md、vitest.config.js 和 package.json,说明如何按项目的规范为文档代码示例编写 Vitest 测试,以及如何运行、观察这些测试。
测试体系的配置依据
在动手写测试之前,先弄清项目里两个关键文件决定了测试的运行规则。
vitest.config.js 的完整配置如下:
import { defineConfig } from 'vitest/config' export default defineConfig({ test: { include: ['tests/**/*.test.js'], globals: false, environment: 'node' } })这三项配置直接约束了测试文件的写法:
include: ['tests/**/*.test.js']:只有tests/目录下匹配*.test.js的文件会被执行,新测试必须放在这个位置才会被收集;globals: false:describe、it、expect不会自动注入全局作用域,每个测试文件必须显式导入,否则测试根本无法运行;environment: 'node':测试默认在 Node.js 环境运行,document、window等浏览器对象不可用——这决定了 DOM 相关的文档示例需要特殊处理(见下文)。
依赖方面,package.json 在devDependencies中声明了三个包:
"devDependencies": { "@vitest/coverage-v8": "^4.0.16", "jsdom": "^27.4.0", "vitest": "^4.0.16" }vitest是测试运行器,jsdom用于个别需要 DOM 环境的测试,@vitest/coverage-v8提供覆盖率报告能力。首次拿到仓库后先运行npm install安装这些开发依赖,再执行测试命令。
测试的目录结构与命名规则
CONTRIBUTING.md 规定测试按概念(concept)组织,每个概念的测试放在以概念名命名的子目录中:
tests/ ├── call-stack/ │ └── call-stack.test.js ├── primitive-types/ │ └── primitive-types.test.js └── ...新增一个概念文档的代码示例时,按同样的规则创建tests/{concept-name}/{concept-name}.test.js。当前仓库中实际的结构比示例更深一层,例如tests/fundamentals/call-stack/、tests/async-javascript/callbacks/、tests/advanced-topics/error-handling/,均遵循“按概念分组 +{concept-name}.test.js命名”的约定。
编写测试:把 console.log 示例转成断言
CONTRIBUTING.md 给出的核心规则是:文档里用console.log展示输出,测试里就必须用expect断言同一个输出。以 docs/concepts/primitive-types.mdx 开头的示例为例,文档中是:
const str = "hello"; console.log(typeof str); // "string"对应的测试写法就是把预期输出写成断言:
import { describe, it, expect } from 'vitest' describe('Primitive Types', () => { it('should return string type', () => { expect(typeof "hello").toBe("string") }) })第一行的显式导入是强制要求,对应globals: false的配置。一个完整的测试文件可以参考 tests/fundamentals/call-stack/call-stack.test.js:它把文档中“嵌套函数调用”的示例代码原样搬进it块,最后用expect(printSquare(4)).toBe(16)验证示例声称的输出。
错误路径用 toThrow 断言
文档中“这个操作会抛出错误”的示例,不能用普通断言覆盖,CONTRIBUTING.md 要求使用expect(() => { ... }).toThrow()。项目中实际用法见 tests/advanced-topics/data-structures/data-structures.test.js:
const weakMap = new WeakMap() // Cannot use primitives as keys expect(() => weakMap.set('string', 'value')).toThrow(TypeError)这里验证的正是文档里“WeakMap 的 key 必须是对象”这一行为说明。
浏览器示例:跳过或切换到 jsdom
由于environment: 'node',涉及document、window的文档示例默认无法测试,CONTRIBUTING.md 的规则是跳过这类示例(对应测试文件里保留纯 Node 可执行的.test.js,不写 DOM 部分)。如果确实需要为 DOM 示例写测试,项目内的做法是创建单独的.dom.test.js文件,并用 Vitest 的注释 pragma 把该文件切到 jsdom 环境。例如 tests/async-javascript/callbacks/callbacks.dom.test.js 的文件头:
/** * @vitest-environment jsdom */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'该文件在beforeEach里用document.createElement('button')构建 DOM 节点,再用button.click()模拟事件,验证文档中事件回调示例的输出。注意文件名必须仍然匹配*.test.js(.dom.test.js满足),否则不会被include规则收集。
严格模式的行为差异
CONTRIBUTING.md 特别提醒:Vitest 以严格模式运行测试,因此在非严格模式下“静默失败”的操作(比如给只读属性赋值),在测试中会抛出TypeError。写测试时如果某个示例在浏览器控制台里不报错、但测试里抛错了,先按这条规则判断,而不是怀疑文档示例写错。
运行测试并验证结果
package.json 的scripts提供了三个入口:
# 一次性运行全部测试 npm test # watch 模式,文件变化后自动重跑 npm run test:watch # 运行测试并生成覆盖率报告 npm run test:coverage它们分别对应vitest run、vitest和vitest run --coverage。日常验证用npm test即可:它会执行tests/下所有匹配*.test.js的文件,任何一个断言与实际行为不符都会让对应测试失败,失败信息中能看到具体是哪个describe/it块、哪一行expect不成立——据此回到对应概念文档核对示例代码。修改测试文件本身时,npm run test:watch免去重复敲命令;npm run test:coverage则用于查看哪些文档示例的行为被测试覆盖到。
写作测试时的检查清单
按 CONTRIBUTING.md 的规则,新写一个测试文件前确认四件事:
- 文件位于
tests/{concept-name}/下且命名为{concept-name}.test.js,能被tests/**/*.test.js匹配; - 第一行显式
import { describe, it, expect } from 'vitest'; - 文档示例的每个预期输出都转成了
expect断言,错误路径用toThrow(); - 涉及 DOM/window 的示例要么不写,要么放进带
@vitest-environment jsdompragma 的.dom.test.js文件。
全部满足后运行npm test,全部通过即说明这批文档代码示例的行为与文档描述一致。
【免费下载链接】33-js-concepts📜 33 JavaScript concepts every developer should know.项目地址: https://gitcode.com/GitHub_Trending/33/33-js-concepts
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考