请问vitest这里配置context 的目的是什么呢?

我见测试框架vitest使用中,有如下的示例:

import { beforeEach, it } from 'vitest'

beforeEach(async (context) => {
  // extend context
  context.foo = 'bar'
})

it('should work', (context) => {
  console.log(context.foo) //  'bar'
})

有2个问题想要咨询:
1)请问这里配置context 的目的是什么呢?

2)有报错:

类型“TaskContext<Test<object> | Custom<object>> & TestContext & object”上不存在属性“foo”。ts(2339)

image.png

阅读 1.2k
avatarAI BotBETA

1)请问这里配置context 的目的是什么呢?

在 Vitest 中,context 对象被用于在测试钩子(如 beforeEachafterEach 等)和测试函数(如 ittest)之间共享状态或数据。配置 context 的主要目的是提供一种机制,允许你在测试的某个阶段(如准备阶段)设置一些状态或数据,然后在测试函数或其他钩子中访问这些数据。这在处理需要在多个测试之间保持状态的场景时非常有用,比如模拟数据、设置全局变量、配置测试环境等。

2)关于报错:“类型“TaskContext<Test<object> | Custom<object>> & TestContext & object”上不存在属性“foo”。ts(2339)”

这个错误是因为 TypeScript 编译器在编译时无法识别 context 对象上自定义的属性 foo。在 TypeScript 中,为了安全地扩展类型或对象,你需要显式地声明这些额外的属性。对于 Vitest 的 context 对象,虽然它允许你在运行时动态添加属性,但 TypeScript 需要你提前声明这些属性以避免类型错误。

为了解决这个问题,你可以通过类型断言或扩展类型定义来告诉 TypeScript context 对象上可能存在的额外属性。一个简单的方法是使用类型断言来绕过这个类型检查:

import { beforeEach, it } from 'vitest'

beforeEach(async (context: any) => {
  // 使用 any 类型断言来避免类型错误
  (context as any).foo = 'bar';
})

it('should work', (context: any) => {
  console.log((context as any).foo); // 'bar'
})

然而,更好的做法是为 context 定义一个自定义类型,并在需要时扩展这个类型:

// 定义一个扩展的 Context 类型
interface ExtendedTestContext extends Vitest.TestContext {
  foo?: string;
}

beforeEach(async (context: ExtendedTestContext) => {
  context.foo = 'bar';
})

it('should work', (context: ExtendedTestContext) => {
  console.log(context.foo); // 'bar'
})

请注意,Vitest.TestContext 是 Vitest 提供的类型,用于描述 context 对象的结构。由于 Vitest 的 API 和类型定义可能会随着版本更新而变化,因此建议查看你正在使用的 Vitest 版本的文档以获取准确的类型定义。如果你发现 Vitest.TestContext 不可用,你可能需要根据你的 Vitest 版本进行调整。

2 个回答

先说问题二,你要是读的官方文档的话,紧接着这段的下一段就教你怎么在 TypeScript 声明额外的属性了:

REF: https://vitest.dev/guide/test-context.html#typescript-1

问题一就是个示例,告诉你钩子里可以访问上下文而已。

要是实际需求的话可能存在这样的场景,你有 N 个测试用例,你现在希望记录一下每个用例的耗时。传统方式的话你可以用例里写一段打点计时的代码,有了钩子和上下文的话你就可以这么写了:

beforeEach((context) => {
  context.startAt = new Date();
})

afterEach((context) => {
  const endAt = new Date();
  const used = endAt.getTime() - context.startAt.getTime();
  console.log(`${context.task.name} used ${used}ms.`);
})

这里你可以把 context 想象成 globalThis,但用例里你不能啥玩意儿都往 globalThis 上挂,否则一旦忘记清理了可能副作用就把别的用例搞坏。而 context 就很安全,每个用例都是隔离的,一个用例里你在 context 上随便改,也不会影响另一个用例。

这是 ts 的类型报错,因为 ts 没有办法感知你手动添加的内容,需要你扩展 context 的类型定义。

type MyContext = {
  foo: string
}

beforeEach<MyContext>(async (context) => {
  // extend context
  context.foo = 'bar'
})

it<MyContext>('should work', (context) => {
  console.log(context.foo) //  'bar'
})

image.png

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏