测试上下文
受到 Playwright Fixtures 的启发,Vitest 的测试上下文允许你定义可以在测试中使用的工具函数、状态和 fixture。
用法
每个测试回调函数的第一个参数是测试上下文。
import { it } from 'vitest';
it('should work', ctx => {
// 打印测试的名称
console.log(ctx.task.name);
});
内置测试上下文
context.task
一个只读对象,包含关于测试的元数据。
context.expect
绑定到当前测试的 expect
API:
import { it } from 'vitest';
it('math is easy', ({ expect }) => {
expect(2 + 2).toBe(4);
});
此 API 对于并发运行快照测试非常有用,因为全局的 expect
无法跟踪它们:
import { it } from 'vitest';
it.concurrent('math is easy', ({ expect }) => {
expect(2 + 2).toMatchInlineSnapshot();
});
it.concurrent('math is hard', ({ expect }) => {
expect(2 * 2).toMatchInlineSnapshot();
});
context.skip
跳过后续的测试执行,并将该测试标记为已跳过:
import { expect, it } from 'vitest';
it('math is hard', ({ skip }) => {
skip();
expect(2 + 2).toBe(5);
});
扩展测试上下文
Vitest 提供了两种不同的方法来扩展测试上下文。
test.extend
WARNING
此 API 自 Vitest 0.32.3 起可用。
与 Playwright 类似,你可以使用此方法定义包含自定义 fixture 的 test
API,并在任何地方复用。
例如,我们首先使用两个 fixture todos
和 archive
创建 myTest
。
// my-test.ts
import { test } from 'vitest';
const todos = [];
const archive = [];
export const myTest = test.extend({
todos: async ({ task }, use) => {
// 在每个测试函数之前设置 fixture
todos.push(1, 2, 3);
// 使用 fixture 的值
await use(todos);
// 在每个测试函数之后清理 fixture
todos.length = 0;
},
archive,
});
然后我们可以导入并使用它。
import { expect } from 'vitest';
import { myTest } from './my-test.ts';
myTest('add items to todos', ({ todos }) => {
expect(todos.length).toBe(3);
todos.add(4);
expect(todos.length).toBe(4);
});
myTest('move items from todos to archive', ({ todos, archive }) => {
expect(todos.length).toBe(3);
expect(archive.length).toBe(0);
archive.push(todos.pop());
expect(todos.length).toBe(2);
expect(archive.length).toBe(1);
});
我们还可以通过扩展 myTest
来添加更多 fixture 或覆盖现有的 fixture。
export const myTest2 = myTest.extend({
settings: {
// ...
},
});
测试夹具初始化
Vitest 运行器会根据使用情况智能地初始化 fixture,并将它们注入到测试上下文中。
import { test } from 'vitest';
async function todosFn({ task }, use) {
await use([1, 2, 3]);
}
const myTest = test.extend({
todos: todosFn,
archive: [],
});
// todosFn 不会运行
myTest('', () => {});
myTest('', ({ archive }) => {});
// todosFn 会运行
myTest('', ({ todos }) => {});
WARNING
使用带有 fixture 的 test.extend()
时,应始终使用对象解构模式 { todos }
来访问上下文中的 fixture 函数和测试函数。
TypeScript
要为所有自定义上下文提供 fixture 类型,可以将 fixture 类型作为泛型传递。
interface MyFixtures {
todos: number[];
archive: number[];
}
const myTest = test.extend<MyFixtures>({
todos: [],
archive: [],
});
myTest('', context => {
expectTypeOf(context.todos).toEqualTypeOf<number[]>();
expectTypeOf(context.archive).toEqualTypeOf<number[]>();
});
beforeEach
和 afterEach
每个测试的上下文都是独立的。你可以在 beforeEach
和 afterEach
钩子中访问和扩展它们。
import { beforeEach, it } from 'vitest';
beforeEach(async context => {
// 扩展上下文
context.foo = 'bar';
});
it('should work', ({ foo }) => {
console.log(foo); // 'bar'
});
TypeScript
要为所有自定义上下文提供属性类型,可以通过以下方式扩展 TestContext
类型
declare module 'vitest' {
export interface TestContext {
foo?: string;
}
}
如果你只想为特定的 beforeEach
、afterEach
、it
和 test
钩子函数提供属性类型,你可以将类型作为泛型传递。
interface LocalTestContext {
foo: string;
}
beforeEach<LocalTestContext>(async context => {
// typeof context is 'TestContext & LocalTestContext'
context.foo = 'bar';
});
it<LocalTestContext>('should work', ({ foo }) => {
// typeof foo is 'string'
console.log(foo); // 'bar'
});