配置、环境与第一条测试
Vitest 的第一项设计决定不是“用哪个 matcher”,而是测试在哪个宿主里观察代码。node 只提供 Node 的模块和运行时;jsdom/happy-dom 是 Node 中的 DOM 模拟;Browser Mode 才是在真实浏览器里执行。把环境写错,测试可能在错误的世界里通过,或者在还没有进入业务断言前就失败。
先建立一个可复现的最小项目
下面的实验不依赖仓库里的其他代码。需要 Node 20+,在临时目录执行:
mkdir vitest-lab && cd vitest-lab
npm init -y
npm install -D vitest typescript创建 src/money.ts、src/money.test.ts 和 vitest.config.ts:
// src/money.ts
export function totalInCents(priceInCents: number, taxRate: number): number {
if (!Number.isInteger(priceInCents) || priceInCents < 0) throw new RangeError('price')
if (!Number.isFinite(taxRate) || taxRate < 0 || taxRate > 1) throw new RangeError('tax')
return Math.round(priceInCents * (1 + taxRate))
}// src/money.test.ts
import { describe, expect, it } from 'vitest'
import { totalInCents } from './money'
describe('totalInCents', () => {
it('rounds tax at the boundary', () => {
expect(totalInCents(1999, 0.2)).toBe(2399)
})
it.each([
[1999, -0.1],
[1999, 1.1],
])('rejects tax rate %s', (price, tax) => {
expect(() => totalInCents(price, tax)).toThrow('tax')
})
})// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
environment: 'node',
clearMocks: true,
},
})在 package.json 加上 "test": "vitest run",执行 npm test。这里的断言不检查实现细节,只检查整数输入、税率边界和四舍五入结果;改成 parseFloat 或允许负价都会让测试失败。
环境是每个测试文件的契约
若被测代码读取 document,Node 环境没有它。安装 DOM 模拟后,可以对单个文件声明环境,而不让所有测试都付出 DOM 初始化成本:
npm install -D jsdom// dom.test.ts
// @vitest-environment jsdom
import { expect, it } from 'vitest'
it('creates a named button', () => {
const button = document.createElement('button')
button.textContent = '保存'
document.body.append(button)
expect(document.querySelector('button')?.textContent).toBe('保存')
})jsdom 能证明 DOM 树、事件派发和很多表单状态;它不能证明真实布局、绘制、浏览器权限策略或跨页面导航。happy-dom 通常更快但 API 覆盖不同,选型要以被测能力为准。真实浏览器测试应放到 Browser Mode 或端到端课程,不要把 document.createElement 通过写成“浏览器兼容”。
配置解析失败时先看模块边界
Vitest 复用 Vite 的转换和解析。tsconfig.json 中的 baseUrl 并不会自动变成 Vite 别名;如果项目使用 @/,应在 Vite 配置里明确 alias 或使用 vite-tsconfig-paths,并让构建和测试共用配置。路径在开发服务器能解析而测试报 Cannot find module,说明两个工具看到的模块图不同,修复解析边界比在测试中改相对路径更可靠。
练习:让失败指向正确边界
- 把
totalInCents改成接收小数元,再写一个输入19.99的测试;说明为什么金额内部仍应转换成最小单位。 - 新建
dom.test.ts,分别删除文件头的@vitest-environment jsdom和安装的jsdom,记录两种失败的第一行。它们分别属于“声明缺失”和“依赖缺失”哪一层? - 在
vitest.config.ts加一个@/money导入,先让测试失败,再用 Vite alias 修复。不要通过复制文件或改测试路径绕开模块图。
答案页给出最小改动和预期观察;请先保留自己的失败输出。
本节边界
本节验证的是 Node/jsdom 中的模块和 DOM 契约,不验证真实浏览器布局、网络和 Vue/React 渲染器。涉及这些能力时,必须提升测试层级,而不是继续堆 jsdom 断言。
下一步阅读Mock、Spy 与测试隔离。