Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions api/expect.md
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ test('the number of elements must match exactly', () => {

## toThrowError

- **类型:** `(received: any) => Awaitable<void>`
- **类型:** `(recexpectedeived: any) => Awaitable<void>`

- **别名:** `toThrow`

Expand All @@ -792,7 +792,9 @@ test('the number of elements must match exactly', () => {

- `RegExp`: 错误消息匹配该模式
- `string`: 错误消息包含该子字符串
- `Error`, `AsymmetricMatcher`: 与接收到的对象进行比较,类似于 `toEqual(received)`
- any other value: compare with thrown value using deep equality (similar to `toEqual`)

<!-- TODO: translation -->

:::tip
必须将代码包装在一个函数中,否则错误将无法被捕获,测试将失败。
Expand Down Expand Up @@ -853,6 +855,19 @@ test('throws on pineapples', async () => {

:::

<!-- TODO: translation -->

:::tip
You can also test non-Error values that are thrown:

```ts
test('throws non-Error values', () => {
expect(() => { throw 42 }).toThrowError(42)
expect(() => { throw { message: 'error' } }).toThrowError({ message: 'error' })
})
```
:::

## toMatchSnapshot

- **类型:** `<T>(shape?: Partial<T> | string, hint?: string) => void`
Expand Down
18 changes: 12 additions & 6 deletions api/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Test hooks are called in a stack order ("after" hooks are reversed) by default,

```ts
function beforeEach(
body: () => unknown,
body: (context: TestContext) => unknown,
timeout?: number,
): void
```
Expand Down Expand Up @@ -55,7 +55,7 @@ beforeEach(async () => {

```ts
function afterEach(
body: () => unknown,
body: (context: TestContext) => unknown,
timeout?: number,
): void
```
Expand Down Expand Up @@ -83,7 +83,7 @@ You can also use [`onTestFinished`](#ontestfinished) during the test execution t

```ts
function beforeAll(
body: () => unknown,
body: (context: ModuleContext) => unknown,
timeout?: number,
): void
```
Expand Down Expand Up @@ -123,7 +123,7 @@ beforeAll(async () => {

```ts
function afterAll(
body: () => unknown,
body: (context: ModuleContext) => unknown,
timeout?: number,
): void
```
Expand All @@ -147,7 +147,10 @@ Here the `afterAll` ensures that `stopMocking` method is called after all tests

```ts
function aroundEach(
body: (runTest: () => Promise<void>, context: TestContext) => Promise<void>,
body: (
runTest: () => Promise<void>,
context: TestContext,
) => Promise<void>,
timeout?: number,
): void
```
Expand Down Expand Up @@ -254,7 +257,10 @@ test('insert user', async ({ db, user }) => {

```ts
function aroundAll(
body: (runSuite: () => Promise<void>) => Promise<void>,
body: (
runSuite: () => Promise<void>,
context: ModuleContext,
) => Promise<void>,
timeout?: number,
): void
```
Expand Down
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export default antfu(
'no-self-compare': 'off',
'import/no-mutable-exports': 'off',
'no-restricted-globals': 'off',
'no-throw-literal': 'off',
},
},
)
84 changes: 72 additions & 12 deletions guide/test-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,14 +280,6 @@ const test = baseTest
.extend('simple', () => 'value')
```

Non-function values only support the `injected` option:

```ts
const test = baseTest
.extend('baseUrl', { injected: true }, 'http://localhost:3000')
.extend('defaults', { port: 3000, host: 'localhost' })
```

#### Accessing Other Fixtures

Each fixture can access previously defined fixtures via its first parameter. This works for both function and non-function fixtures:
Expand Down Expand Up @@ -507,15 +499,13 @@ Note that you cannot override non-test fixtures inside `describe` blocks:

```ts
test.describe('a nested suite', () => {
test.override('port', 3000) // throws an error
test.override('port', { scope: 'worker' }, 3000) // throws an error
})
```

Consider overriding it on the top level of the module, or by using [`injected`](#default-fixture-injected) option and providing the value in the project config.

Also note that in [non-isolate](/config/isolate) mode overriding a `worker` fixture will affect the fixture value in all test files running after it was overriden.

<!-- TODO(v5) should this be addressed? force a new worker if worker fixture is overriden? -->
:::

#### Test Scope (Default)
Expand Down Expand Up @@ -833,7 +823,7 @@ Note that you cannot introduce new fixtures inside `test.override`. Extend the t

### Type-Safe Hooks

When using `test.extend`, the extended `test` object provides type-safe `beforeEach` and `afterEach` hooks that are aware of the new context:
When using `test.extend`, the extended `test` object provides type-safe hooks that are aware of the extended context:

```ts
const test = baseTest
Expand All @@ -848,3 +838,73 @@ test.afterEach(({ counter }) => {
console.log('Final count:', counter.value)
})
```

#### Suite-Level Hooks with Fixtures <Version>4.1.0</Version> {#suite-level-hooks}

The extended `test` object also provides [`beforeAll`](/api/hooks#beforeall), [`afterAll`](/api/hooks#afterall), and [`aroundAll`](/api/hooks#aroundall) hooks that can access file-scoped and worker-scoped fixtures:

```ts
const test = baseTest
.extend('config', { scope: 'file' }, () => loadConfig())
.extend('database', { scope: 'file' }, async ({ config }, { onCleanup }) => {
const db = await createDatabase(config)
onCleanup(() => db.close())
return db
})

// Access file-scoped fixtures in suite-level hooks
test.aroundAll(async (runSuite, { database }) => {
await database.transaction(runSuite)
})

test.beforeAll(async ({ database }) => {
await database.createUsers()
})

test.afterAll(async ({ database }) => {
await database.removeUsers()
})
```

::: warning IMPORTANT
Suite-level hooks (`beforeAll`, `afterAll`, `aroundAll`) **must be called on the `test` object returned from `test.extend()`** to have access to the extended fixtures. Using the global `beforeAll`/`afterAll`/`aroundAll` functions will not have access to your custom fixtures:

```ts
import { test as baseTest, beforeAll } from 'vitest'

const test = baseTest
.extend('database', { scope: 'file' }, async ({}, { onCleanup }) => {
const db = await createDatabase()
onCleanup(() => db.close())
return db
})

// ❌ WRONG: Global beforeAll doesn't have access to 'database'
beforeAll(({ database }) => {
// Error: 'database' is undefined
})

// ✅ CORRECT: Use test.beforeAll to access fixtures
test.beforeAll(({ database }) => {
// 'database' is available
})
```

This applies to all suite-level hooks: `beforeAll`, `afterAll`, and `aroundAll`.
:::

::: tip
Suite-level hooks can only access [**file-scoped** and **worker-scoped** fixtures](#fixture-scopes). Test-scoped fixtures are not available in these hooks because they run outside the context of individual tests. If you try to access a test-scoped fixture in a suite-level hook, Vitest will throw an error.

```ts
const test = baseTest
.extend('testFixture', () => 'test-scoped')
.extend('fileFixture', { scope: 'file' }, () => 'file-scoped')

// ❌ Error: test-scoped fixtures not available in beforeAll
test.beforeAll(({ testFixture }) => {})

// ✅ Works: file-scoped fixtures are available
test.beforeAll(({ fileFixture }) => {})
```
:::