vitest/docs/guide/test-context.md
Vladimir 6598fba55b
docs: remove version badges (#5727)
Co-authored-by: Ari Perkkiö <ari.perkkio@gmail.com>
2024-05-15 08:44:37 +02:00

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'
})
```