主要功能
- 与 Vite 通用的配置、转换器、解析器和插件。
- 使用你的应用程序中的相同配置来进行测试!
- 支持测试 Vue、React、Lit 等框架中的组件。
- 开箱即用的 TypeScript / JSX 支持
- ESM 优先,支持模块顶级 await
- 通过 tinypool 使用 Worker 线程尽可能多地并发运行
- 使用 Tinybench 来支持基准测试
- 套件和测试的过滤、超时、并发配置
- 内置 Chai 进行断言 + 与 Jest expect 语法兼容的 API
- 内置用于对象模拟(Mock)的 Tinyspy
- 通过 c8 来输出代码测试覆盖率
- 类似于 Rust 语言的 源码内联测试
- 通过 expect-type 进行类型测试
一套配置可以运用在多种环境
Learn how to write your first test by Video与 Vite 的配置、转换器、解析器和插件通用,将会使用应用程序中的相同配置来运行测试。
了解更多信息 配置 Vitest
监听模式(watch mode)
$ vitest
$ vitest
当你修改源代码或测试文件时,Vitest 智能搜索模块依赖树并只重新运行相关测试,just like how HMR works in Vite!
vitest
在开发环境下默认 启动时使用 监听模式
,在 CI 环境(当 p
出现时)中以 运行模式(run mode)
启动。你可以使用 vitest watch
或 vitest run
明确指定所需的模式。
开箱即用的常见 Web 支持
开箱即用的 ES Module / TypeScript / JSX support / PostCSS
多线程
通过 tinypool 使用 Worker 线程尽可能多地并发运行( Piscina 的轻量级分支),允许多个测试同时运行。Vitest 默认启动多线程,可以通过 CLI 中的 --no-threads
禁用。
Vitest 还隔离了每个测试文件的运行环境,因此一个文件中的运行环境改变不会影响其他文件。可以通过将 --no-isolate
传递给 CLI 来禁用隔离(以正确性换取运行性能)。
测试可过滤
Vitest 提供了许多缩小测试范围的方法,以便在开发过程中加快速度并集中精力。
了解更多信息 测试筛选
同时运行多个测试
在连续测试中使用 .concurrent
将会并发运行它们。
import { describe, it } from "vitest";
// The two tests marked with concurrent will be run in parallel
describe("suite", () => {
it("serial test", async () => {
/* ... */
});
it.concurrent("concurrent test 1", async () => {
/* ... */
});
it.concurrent("concurrent test 2", async () => {
/* ... */
});
});
import { describe, it } from "vitest";
// The two tests marked with concurrent will be run in parallel
describe("suite", () => {
it("serial test", async () => {
/* ... */
});
it.concurrent("concurrent test 1", async () => {
/* ... */
});
it.concurrent("concurrent test 2", async () => {
/* ... */
});
});
如果在测试套件中使用 .concurrent
,则其中的每个测试用例都将并发运行。
import { describe, it } from "vitest";
// All tests within this suite will be run in parallel
describe.concurrent("suite", () => {
it("concurrent test 1", async () => {
/* ... */
});
it("concurrent test 2", async () => {
/* ... */
});
it.concurrent("concurrent test 3", async () => {
/* ... */
});
});
import { describe, it } from "vitest";
// All tests within this suite will be run in parallel
describe.concurrent("suite", () => {
it("concurrent test 1", async () => {
/* ... */
});
it("concurrent test 2", async () => {
/* ... */
});
it.concurrent("concurrent test 3", async () => {
/* ... */
});
});
你还可以将 .skip
、.only
和 .todo
用于并发测试套件和测试用例。
了解更多信息 API 索引
快照
兼容 Jest 快照测试 功能。
import { expect, it } from "vitest";
it("renders correctly", () => {
const result = render();
expect(result).toMatchSnapshot();
});
import { expect, it } from "vitest";
it("renders correctly", () => {
const result = render();
expect(result).toMatchSnapshot();
});
了解更多信息 快照
Chai 和 Jest 的 expect 语法兼容
内置 Chai 进行断言和与 Jest expect 兼容的 APIs
注意,如果你正在使用添加匹配器的第三方库,将 test.globals
设置为 true
将提供更好的兼容性。
对象模拟(Mocking)
内置 Tinyspy 用于在 vi
对象上使用 jest
兼容的 API 进行对象模拟。
import { expect, vi } from "vitest";
const fn = vi.fn();
fn("hello", 1);
expect(vi.isMockFunction(fn)).toBe(true);
expect(fn.mock.calls[0]).toEqual(["hello", 1]);
fn.mockImplementation((arg) => arg);
fn("world", 2);
expect(fn.mock.results[1].value).toBe("world");
import { expect, vi } from "vitest";
const fn = vi.fn();
fn("hello", 1);
expect(vi.isMockFunction(fn)).toBe(true);
expect(fn.mock.calls[0]).toEqual(["hello", 1]);
fn.mockImplementation((arg) => arg);
fn("world", 2);
expect(fn.mock.results[1].value).toBe("world");
Vitest 支持 happy-dom 或 jsdom 来模拟 DOM 和浏览器 API。Vitest 并不内置它们,所以你可能需要安装:
$ npm i -D happy-dom
# or
$ npm i -D jsdom
$ npm i -D happy-dom
# or
$ npm i -D jsdom
然后,更改 environment
配置文件中的选项:
// vite.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "happy-dom", // or 'jsdom', 'node'
},
});
// vite.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "happy-dom", // or 'jsdom', 'node'
},
});
了解更多信息 模拟对象
测试覆盖率
Vitest 通过 c8
支持本机代码覆盖率。同时也支持 istanbul
。
{
"scripts": {
"test": "vitest",
"coverage": "vitest run --coverage"
}
}
{
"scripts": {
"test": "vitest",
"coverage": "vitest run --coverage"
}
}
可以在配置文件中设置 test.coverage
选项来配置它:
// vite.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
coverage: {
reporter: ["text", "json", "html"],
},
},
});
// vite.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
coverage: {
reporter: ["text", "json", "html"],
},
},
});
了解更多信息 测试覆盖率
源码内联测试
Vitest 还提供了一种方式,可以运行与你的代码实现放在一起的测试,类似 Rust's module tests.
这使得测试与实现共享相同的闭包,并且能够在不导出的情况下针对私有状态进行测试。同时,它也使开发更加接近反馈循环。
// src/index.ts
// the implementation
export function add(...args: number[]) {
return args.reduce((a, b) => a + b, 0);
}
// in-source test suites
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest;
it("add", () => {
expect(add()).toBe(0);
expect(add(1)).toBe(1);
expect(add(1, 2, 3)).toBe(6);
});
}
// src/index.ts
// the implementation
export function add(...args: number[]) {
return args.reduce((a, b) => a + b, 0);
}
// in-source test suites
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest;
it("add", () => {
expect(add()).toBe(0);
expect(add(1)).toBe(1);
expect(add(1, 2, 3)).toBe(6);
});
}
了解更多信息 源码内联测试
基准测试 experimental
从 Vitest 0.23.0 开始,你可以使用 bench
运行基准测试通过 Tinybench 函数来比较基准测试结果。
import { bench, describe } from "vitest";
describe("sort", () => {
bench("normal", () => {
const x = [1, 5, 4, 2, 3];
x.sort((a, b) => {
return a - b;
});
});
bench("reverse", () => {
const x = [1, 5, 4, 2, 3];
x.reverse().sort((a, b) => {
return a - b;
});
});
});
import { bench, describe } from "vitest";
describe("sort", () => {
bench("normal", () => {
const x = [1, 5, 4, 2, 3];
x.sort((a, b) => {
return a - b;
});
});
bench("reverse", () => {
const x = [1, 5, 4, 2, 3];
x.reverse().sort((a, b) => {
return a - b;
});
});
});
类型测试 experimental
从 Vitest 0.25.0 开始,你可以 编写测试 来捕获类型回归。 Vitest 附带 expect-type
包,为你提供类似且易于理解的 API。
import { assertType, expectTypeOf } from "vitest";
import { mount } from "./mount.js";
test("my types work properly", () => {
expectTypeOf(mount).toBeFunction();
expectTypeOf(mount).parameter(0).toMatchTypeOf<{ name: string }>();
// @ts-expect-error name is a string
assertType(mount({ name: 42 }));
});
import { assertType, expectTypeOf } from "vitest";
import { mount } from "./mount.js";
test("my types work properly", () => {
expectTypeOf(mount).toBeFunction();
expectTypeOf(mount).parameter(0).toMatchTypeOf<{ name: string }>();
// @ts-expect-error name is a string
assertType(mount({ name: 42 }));
});