mirror of
https://github.com/vitest-dev/vitest.git
synced 2025-12-08 18:26:03 +00:00
249 lines
5.1 KiB
Markdown
249 lines
5.1 KiB
Markdown
---
|
|
title: Test Context | Guide
|
|
---
|
|
|
|
# Test Context
|
|
|
|
Inspired by [Playwright Fixtures](https://playwright.dev/docs/test-fixtures), Vitest's test context allows you to define utils, states, and fixtures that can be used in your tests.
|
|
|
|
## Usage
|
|
|
|
The first argument for each test callback is a test context.
|
|
|
|
```ts twoslash
|
|
import { it } from 'vitest'
|
|
|
|
it('should work', (ctx) => {
|
|
// prints name of the test
|
|
console.log(ctx.task.name)
|
|
})
|
|
```
|
|
|
|
## Built-in Test Context
|
|
|
|
#### `context.task`
|
|
|
|
A readonly object containing metadata about the test.
|
|
|
|
#### `context.expect`
|
|
|
|
The `expect` API bound to the current test:
|
|
|
|
```ts twoslash
|
|
import { it } from 'vitest'
|
|
|
|
it('math is easy', ({ expect }) => {
|
|
expect(2 + 2).toBe(4)
|
|
})
|
|
```
|
|
|
|
This API is useful for running snapshot tests concurrently because global expect cannot track them:
|
|
|
|
```ts twoslash
|
|
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`
|
|
|
|
Skips subsequent test execution and marks test as skipped:
|
|
|
|
```ts
|
|
import { expect, it } from 'vitest'
|
|
|
|
it('math is hard', ({ skip }) => {
|
|
skip()
|
|
expect(2 + 2).toBe(5)
|
|
})
|
|
```
|
|
|
|
## Extend Test Context
|
|
|
|
Vitest provides two different ways to help you extend the test context.
|
|
|
|
### `test.extend`
|
|
|
|
Like [Playwright](https://playwright.dev/docs/api/class-test#test-extend), you can use this method to define your own `test` API with custom fixtures and reuse it anywhere.
|
|
|
|
For example, we first create `myTest` with two fixtures, `todos` and `archive`.
|
|
|
|
```ts
|
|
// my-test.ts
|
|
import { test } from 'vitest'
|
|
|
|
const todos = []
|
|
const archive = []
|
|
|
|
export const myTest = test.extend({
|
|
todos: async ({}, use) => {
|
|
// setup the fixture before each test function
|
|
todos.push(1, 2, 3)
|
|
|
|
// use the fixture value
|
|
await use(todos)
|
|
|
|
// cleanup the fixture after each test function
|
|
todos.length = 0
|
|
},
|
|
archive
|
|
})
|
|
```
|
|
|
|
Then we can import and use it.
|
|
|
|
```ts
|
|
import { expect } from 'vitest'
|
|
import { myTest } from './my-test.js'
|
|
|
|
myTest('add items to todos', ({ todos }) => {
|
|
expect(todos.length).toBe(3)
|
|
|
|
todos.push(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)
|
|
})
|
|
```
|
|
|
|
We can also add more fixtures or override existing fixtures by extending `myTest`.
|
|
|
|
```ts
|
|
export const myTest2 = myTest.extend({
|
|
settings: {
|
|
// ...
|
|
}
|
|
})
|
|
```
|
|
|
|
#### Fixture initialization
|
|
|
|
Vitest runner will smartly initialize your fixtures and inject them into the test context based on usage.
|
|
|
|
```ts
|
|
import { test } from 'vitest'
|
|
|
|
async function todosFn({ task }, use) {
|
|
await use([1, 2, 3])
|
|
}
|
|
|
|
const myTest = test.extend({
|
|
todos: todosFn,
|
|
archive: []
|
|
})
|
|
|
|
// todosFn will not run
|
|
myTest('', () => {})
|
|
myTest('', ({ archive }) => {})
|
|
|
|
// todosFn will run
|
|
myTest('', ({ todos }) => {})
|
|
```
|
|
|
|
::: warning
|
|
When using `test.extend()` with fixtures, you should always use the object destructuring pattern `{ todos }` to access context both in fixture function and test function.
|
|
:::
|
|
|
|
#### Automatic fixture
|
|
|
|
Vitest also supports the tuple syntax for fixtures, allowing you to pass options for each fixture. For example, you can use it to explicitly initialize a fixture, even if it's not being used in tests.
|
|
|
|
```ts
|
|
import { test as base } from 'vitest'
|
|
|
|
const test = base.extend({
|
|
fixture: [
|
|
async ({}, use) => {
|
|
// this function will run
|
|
setup()
|
|
await use()
|
|
teardown()
|
|
},
|
|
{ auto: true } // Mark as an automatic fixture
|
|
],
|
|
})
|
|
|
|
test('', () => {})
|
|
```
|
|
|
|
#### TypeScript
|
|
|
|
To provide fixture types for all your custom contexts, you can pass the fixtures type as a generic.
|
|
|
|
```ts
|
|
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` and `afterEach`
|
|
|
|
The contexts are different for each test. You can access and extend them within the `beforeEach` and `afterEach` hooks.
|
|
|
|
```ts
|
|
import { beforeEach, it } from 'vitest'
|
|
|
|
beforeEach(async (context) => {
|
|
// extend context
|
|
context.foo = 'bar'
|
|
})
|
|
|
|
it('should work', ({ foo }) => {
|
|
console.log(foo) // 'bar'
|
|
})
|
|
```
|
|
|
|
#### TypeScript
|
|
|
|
To provide property types for all your custom contexts, you can aggregate the `TestContext` type by adding
|
|
|
|
```ts
|
|
declare module 'vitest' {
|
|
export interface TestContext {
|
|
foo?: string
|
|
}
|
|
}
|
|
```
|
|
|
|
If you want to provide property types only for specific `beforeEach`, `afterEach`, `it` and `test` hooks, you can pass the type as a generic.
|
|
|
|
```ts
|
|
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'
|
|
})
|
|
```
|