不同的测试类型与方案
使用 Vitest
Vitest 是一款高性能、与 Vite 深度集成的测试框架,适合现代前端开发中的多种测试需求:
1. 常见的单元测试需求
-
适用场景:
- 验证函数、模块或组件的正确性。
- 对独立的逻辑单元进行验证,例如工具函数、数据处理逻辑等。
-
示例:
import { describe, it, expect } from 'vitest'; const add = (a: number, b: number) => a + b; describe('add function', () => { it('adds two numbers correctly', () => { expect(add(1, 2)).toBe(3); }); });
2. 基准测试(Benchmark,侧重比较性能结果)
-
适用场景:
- 比较不同实现的性能差异。
- 用于性能瓶颈分析。
-
示例:
import { bench } from 'vitest'; bench('Array#map', () => { [1, 2, 3].map(x => x * 2); }); bench('For loop', () => { const result = []; for (let i = 0; i < 3; i++) { result.push(i * 2); } });
3. 覆盖率测试
-
适用场景:
- 确保代码的每个部分都被测试到。
- 生成覆盖率报告,用于评估测试质量。
-
示例:
- 在
vitest.config.ts
中启用覆盖率:
export default defineConfig({ test: { coverage: { reporter: ['text', 'html'], // 覆盖率报告输出格式 }, }, });
4. 类型测试
-
适用场景:
- 测试 TypeScript 类型定义的行为。
- 验证类型推断是否符合预期。
-
示例:
import { expectTypeOf } from 'vitest'; const add = (a: number, b: number) => a + b; expectTypeOf(add).toBeFunction(); expectTypeOf(add).parameters.toEqualTypeOf<[number, number]>();
5. 源码内联测试
-
适用场景:
- 在源码文件中直接编写与其相关的测试。
- 适合验证小型模块的行为。
-
示例: 在源码文件中:
export function multiply(a: number, b: number): number { return a * b; } // @vitest-environment happy-dom if (import.meta.vitest) { it('multiplies two numbers', () => { expect(multiply(2, 3)).toBe(6); }); }
6. 测试快照
-
适用场景:
- 测试组件或对象的结构在多次运行时保持一致。
- 检测 UI 输出是否发生意外变化。
-
示例:
import { describe, it, expect } from 'vitest'; const generateObject = () => ({ foo: 'bar', baz: 'qux' }); describe('Snapshot Test', () => { it('matches the snapshot', () => { expect(generateObject()).toMatchSnapshot(); }); });
使用 test-storybook
test-storybook 是专门为组件开发设计的测试工具,基于 Storybook 环境,结合 UI 的 DOM 结构测试。
适用场景
- 组件交互测试
- 验证组件在不同交互状态下的表现。
- 基于 DOM 的 UI 测试
- 直接测试组件渲染输出是否符合预期。
- 与 Storybook 整合
- 重用 Storybook 的故事作为测试用例。
使用其他自动化截图对比测试工具和方案
对于复杂的 UI 组件(如基于 Canvas 的渲染组件),可以通过截图对比工具检测输出是否发生变化。
适用场景
- 测试基于 Canvas 或 SVG 的组件渲染结果。
- 用于检测视觉回归问题。
常见工具
- Percy
- 自动化截图对比工具,适合与 CI/CD 集成。
- Puppeteer + Pixelmatch
- 自定义截图测试方案,基于 Headless 浏览器和像素比对。
方案示例
-
基于 Puppeteer 和 Pixelmatch:
const puppeteer = require('puppeteer'); const pixelmatch = require('pixelmatch'); const { PNG } = require('pngjs'); const fs = require('fs'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('http://localhost:3000'); const screenshot = await page.screenshot(); const baseline = PNG.sync.read(fs.readFileSync('./baseline.png')); const comparison = PNG.sync.read(screenshot); const diff = new PNG({ width: baseline.width, height: baseline.height }); const mismatchedPixels = pixelmatch( baseline.data, comparison.data, diff.data, baseline.width, baseline.height, { threshold: 0.1 } ); fs.writeFileSync('./diff.png', PNG.sync.write(diff)); console.log(
Mismatched Pixels: ${mismatchedPixels}
); await browser.close(); })();
对比总结
测试类型 | 工具 | 优势 | 适用场景 |
---|---|---|---|
单元测试 | Vitest | 快速、简单、支持 TS 和内联测试 | 函数、逻辑模块、简单组件 |
性能测试 | Vitest (Benchmark) | 可对比不同算法或实现的性能 | 性能优化和瓶颈分析 |
覆盖率测试 | Vitest | 自动生成覆盖率报告 | 评估测试覆盖范围 |
类型测试 | Vitest | 确保类型定义的正确性和推断结果 | TypeScript 项目 |
测试快照 | Vitest | 快速验证结构或输出的一致性 | UI 渲染或结构化数据测试 |
DOM 组件测试 | test-storybook | 整合 Storybook,专注 UI 和交互状态 | 组件交互测试 |
截图对比测试(视觉回归测试) | Puppeteer + Pixelmatch | 可用于复杂 Canvas 或 SVG 的渲染测试 | 基于视觉的回归问题检测 |
通过这些工具和方案,你可以针对不同测试类型选择合适的工具,高效完成各类测试需求。
Vue3 项目快速上手 Vitest
将 Vitest 安装到项目
Vitest 需要 Vite >= v5.0.0 且 Node >= v18.0.0
npm:
npm install -D vitest
yarn:
yarn add -D vitest
pnpm:
pnpm add -D vitest
进行推荐的基础配置
在
vite.config.ts
的顶部添加/// <reference types="vitest/config" />
三斜杠指令以提供TS类型提示。
然后在vite.config.ts
的defineConfig
方法的入参对象中添加test
属性,如下所示为个人推荐的配置:
/// <reference types="vitest/config" />
import { defineConfig } from 'vite'
// import ...
export default defineConfig({
// ...
test: {
// Vitest 配置
coverage: {
// 覆盖率测试 相关配置
provider: 'v8', // 覆盖率测试的引擎
reporter: ['text', 'json', 'html'], // 测试报告的输出文件类型
include: ['src/utils', 'src/components'], // 覆盖率测试的范围
},
includeSource: ['src/utils/**/*.{js,ts}', 'src/components/**/*.{js,ts}'], // 源码内联测试的范围
},
})
还可以在package.json
中配置常用的测试脚本:
{
"scripts": {
"test": "vitest",
"coverage": "vitest run --coverage",
}
}
常用的终端命令
-
vitest
:在当前目录中启动Vitest。在开发环境会自动进入监听(watch
)模式,在CI环境会自动进入运行(run
)模式。默认的包含与排除规则:
include: **/*.{test,spec}.?(c|m)[jt]s?(x) exclude: **/node_modules/**, **/dist/**, **/cypress/**, **/.{idea,git,cache,output,temp}/**, **/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*
可以添加
--ui
选项来启用 Vitest UI ,这个界面是可选的,可以通过以下命令安装:npm i -D @vitest/ui
-
vitest run
:在没有监听模式的情况下执行单次运行。 -
vitest bench
:仅运行基准测试
,比较性能结果。 -
vitest list
:该命令继承所有的vitest
选项以打印所有匹配测试的列表。可以传递
--json
标志以 JSON 格式打印测试,也可以将其保存在单独的文件中:vitest list filename.spec.ts -t="some-test" --json=./file.json
describe > some-test describe > some-test > test 1 describe > some-test > test 2
如果
--json
标志没有接收到值,它将把 JSON 输出到 stdout 中。还可以传递
--filesOnly
标志来仅打印测试文件:vitest list --filesOnly
tests/test1.test.ts tests/test2.test.ts
Globals 配置与自动导入
Vitest通过其vi
辅助工具提供实用功能来帮助我们进行测试。默认情况下,vitest
不显式提供全局 API。而需要从vitest
中导入:
import { vi } from 'vitest';
也可以启用Globals 配置来全局访问它:
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true,
},
})
为了可以让全局 API 支持 TypeScript,请将 vitest/globals
添加到 tsconfig.json
中的 types
选项中:
{
"compilerOptions": {
"types": ["vitest/globals"]
}
}
如果已经在项目中使用 unplugin-auto-import
,也可以直接用它来自动导入这些 API:
import { defineConfig } from 'vitest/config'
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
AutoImport({
imports: ['vitest'], // 添加 vitest
dts: true, // generate TypeScript declaration
}),
],
})
基于 Vitest 的常用测试
单元测试
在相应目录下创建xxx.test.js/ts
文件来编写相关的测试用例。
简单示例请参考Vitest - 快速起步 - 编写测试。
了解更多关于 Vitest 的使用,请参考 API 索引 部分。
覆盖率测试
基础配置已在上文Vue3 项目快速上手 Vitest => 进行推荐的基础配置
说明,其他详见官网文档Vitest - 测试覆盖率。
测试快照
官网文档写的非常简洁明了,建议直接看官方文档[Vitest - 测试快照]。
类型测试
详见官网文档[Vitest - 类型测试]。
源码内联测试
Vitest 还提供了一种方式,可以运行与你的代码实现放在一起的测试,就像是 Rust 语言的模块测试一样。
这允许测试与实现共享相同的闭包,并且能够在不导出的情况下针对私有状态进行测试。同时,它也使开发更加接近反馈循环。
官网文档写的非常简洁明了,建议直接看官方文档[Vitest - 源码内联测试]。
Comments NOTHING