···4949Don't forget the `await` before `expect`. Vitest will detect unawaited assertions and print a warning at the end of the test, but it's best to always include `await` explicitly. Vitest will also wait for all pending promises in `Promise.all` before starting the next test, but relying on this behavior makes tests harder to understand.
5050:::
51515252+## Assertion Counting
5353+5454+With async code, there's a subtle risk: an assertion inside a callback or `.then()` chain might never execute, and the test would still pass because no assertion failed. [`expect.hasAssertions()`](/api/expect#hasassertions) guards against this by verifying that at least one assertion ran during the test:
5555+5656+```js
5757+test('callback is invoked', async () => {
5858+ expect.hasAssertions()
5959+6060+ const data = await fetchData()
6161+ data.items.forEach((item) => {
6262+ expect(item.id).toBeDefined()
6363+ })
6464+ // if data.items is empty, the test fails instead of silently passing
6565+})
6666+```
6767+6868+When you know exactly how many assertions should run, [`expect.assertions(n)`](/api/expect#assertions) is more precise:
6969+7070+```js
7171+test('both callbacks are called', async () => {
7272+ expect.assertions(2)
7373+7474+ await Promise.all([
7575+ fetchUser(1).then(user => expect(user.name).toBe('Alice')),
7676+ fetchUser(2).then(user => expect(user.name).toBe('Bob')),
7777+ ])
7878+})
7979+```
8080+8181+In most cases, `async`/`await` with direct assertions is clear enough and you don't need assertion counting. It's most useful when assertions are inside callbacks, loops, or conditional branches where you want to guarantee they actually executed.
8282+8383+::: tip
8484+If you want every test in your project to require at least one assertion, enable [`expect.requireAssertions`](/config/expect#expect-requireassertions) in your config instead of adding `expect.hasAssertions()` to each test manually.
8585+:::
8686+5287## Callbacks
53885489Some older APIs use callbacks instead of promises. Since Vitest works with promises, the simplest approach is to wrap the callback in a `Promise`:
···6810369104This pattern works for any callback-based API. Pass `resolve` as the success callback, and the test will wait until the callback is invoked.
70105106106+::: tip
107107+Most modern Node.js APIs (such as `fs/promises` and `fetch`) support promises natively, so you can use `async`/`await` directly. The callback wrapping pattern above is mainly useful for older libraries that haven't adopted promises yet.
108108+:::
109109+71110## Timeouts
7211173112By default, each test has a 5-second timeout. If a test takes longer than that (perhaps because a promise never resolves, or a network request hangs), it will fail with a timeout error. This prevents your test suite from getting stuck indefinitely.
···91130 },
92131})
93132```
9494-9595-## Concurrent Tests
9696-9797-By default, tests within a file run one after another. This is usually what you want, especially when tests share setup code. But if you have many independent async tests that each spend most of their time waiting (on network, disk, timers, etc.), running them concurrently with [`test.concurrent`](/api/test#concurrent) can significantly speed things up:
9898-9999-```js
100100-test.concurrent('first async test', async () => {
101101- const result = await fetchUser(1)
102102- expect(result.name).toBe('Alice')
103103-})
104104-105105-test.concurrent('second async test', async () => {
106106- const result = await fetchUser(2)
107107- expect(result.name).toBe('Bob')
108108-})
109109-```
110110-111111-See the [Parallelism](/guide/parallelism) guide for the full picture of how Vitest runs tests in parallel, both across files and within them.
112133113134## Unhandled Rejections
114135
+13-1
docs/guide/learn/debugging-tests.md
···5353})
5454```
55555656+If you have many failures and want to focus on the first one, use [`--bail`](/config/bail) to stop after a set number of failures:
5757+5858+```bash
5959+vitest --bail 1
6060+```
6161+5662If the test passes when run alone but fails when run with others, you have a test isolation problem (more on that below). If it fails even when run alone, the issue is in the test itself or the code it's testing.
57635864## Common Causes of Failures
···188194189195### Attaching a Debugger
190196191191-For more complex issues where you need to step through code line by line, you can attach a debugger. See the [Debugging](/guide/debugging) guide for setup instructions for VS Code, IntelliJ, and Chrome DevTools.
197197+For more complex issues where you need to step through code line by line, you can run Vitest with the `--inspect-brk` flag and attach a debugger. The `--no-file-parallelism` flag ensures tests run in the main thread so breakpoints work reliably:
198198+199199+```bash
200200+vitest --inspect-brk --no-file-parallelism
201201+```
202202+203203+Then attach from VS Code, IntelliJ, or Chrome DevTools (`chrome://inspect`). See the [Debugging](/guide/debugging) guide for detailed setup instructions for each editor.
192204193205## Getting Help
194206
+47-1
docs/guide/learn/matchers.md
···5050})
5151```
52525353+There's also [`toStrictEqual`](/api/expect#tostrictequal), which is stricter than `toEqual` in three ways: it checks `undefined` properties, distinguishes sparse arrays from `undefined` values, and verifies that objects have the same type (not just the same shape):
5454+5555+```js
5656+test('toEqual vs toStrictEqual', () => {
5757+ // toEqual ignores undefined properties
5858+ expect({ a: 1 }).toEqual({ a: 1, b: undefined })
5959+6060+ // toStrictEqual catches them
6161+ expect({ a: 1 }).not.toStrictEqual({ a: 1, b: undefined })
6262+6363+ // toEqual doesn't check object types
6464+ class User {
6565+ constructor(name) {
6666+ this.name = name
6767+ }
6868+ }
6969+ expect(new User('Alice')).toEqual({ name: 'Alice' })
7070+ expect(new User('Alice')).not.toStrictEqual({ name: 'Alice' })
7171+})
7272+```
7373+5374::: tip
5454-A good rule of thumb: use `toBe` for primitives (numbers, strings, booleans) and `toEqual` for objects and arrays.
7575+A good rule of thumb: use `toBe` for primitives (numbers, strings, booleans), `toEqual` for comparing structure, and `toStrictEqual` when you also care about types and explicit `undefined` values.
5576:::
56775778You can also negate any matcher by inserting `.not` before it. This is useful when you want to verify that something is *not* the case:
···192213 expect(user).toHaveProperty('address.zip')
193214})
194215```
216216+217217+## Asymmetric Matchers
218218+219219+Sometimes you don't know the exact value, but you know its type or shape. Asymmetric matchers let you describe what a value should *look like* without pinning down the exact content. They work inside any matcher that does deep comparison, like `toEqual` or `toMatchObject`:
220220+221221+```js
222222+test('user has the right shape', () => {
223223+ const user = createUser('Alice')
224224+225225+ expect(user).toEqual({
226226+ id: expect.any(Number),
227227+ name: 'Alice',
228228+ email: expect.stringContaining('@'),
229229+ roles: expect.arrayContaining(['viewer']),
230230+ })
231231+})
232232+```
233233+234234+The most common asymmetric matchers are:
235235+236236+- [`expect.any(Constructor)`](/api/expect#expect-any) matches any value created with the given constructor (e.g., `Number`, `String`, `Array`)
237237+- [`expect.stringContaining(str)`](/api/expect#expect-stringcontaining) matches a string that includes the given substring
238238+- [`expect.stringMatching(regex)`](/api/expect#expect-stringmatching) matches a string against a regular expression
239239+- [`expect.arrayContaining(arr)`](/api/expect#expect-arraycontaining) matches an array that includes all items in the expected array (order doesn't matter, extra items are allowed)
240240+- [`expect.objectContaining(obj)`](/api/expect#expect-objectcontaining) matches an object that includes at least the specified properties
195241196242## Exceptions
197243
+6-2
docs/guide/learn/mock-functions.md
···114114 expect(greet).toHaveBeenCalledWith('Alice')
115115 expect(greet).toHaveBeenCalledWith('Bob', 'Charlie')
116116117117+ // Check the arguments of a specific call by position
118118+ expect(greet).toHaveBeenNthCalledWith(1, 'Alice')
119119+ expect(greet).toHaveBeenLastCalledWith('Bob', 'Charlie')
120120+117121 // Access the raw call data
118122 expect(greet.mock.calls).toEqual([
119123 ['Alice'],
···267271[`vi.mock`](/api/vi#vi-mock) calls are hoisted to the top of the file. They run before any imports. This means the mocked version is in place by the time your test code runs.
268272:::
269273270270-::: tip
271271-Notice that we pass `import('./db.js')` instead of a plain string `'./db.js'`. When you use `import()`, TypeScript can infer the module's types, so the factory function's return value is type-checked and `importOriginal` returns the correctly typed module. As a bonus, if you rename or move the file in your IDE, the import path will be updated automatically. If you use a string, you lose both the type safety and the automatic refactoring.
274274+::: warning
275275+Always pass `import('./db.js')` rather than a plain string `'./db.js'`. When you use `import()`, TypeScript can infer the module's types, so the factory function's return value is type-checked and `importOriginal` returns the correctly typed module. As a bonus, if you rename or move the file in your IDE, the import path will be updated automatically. If you use a string, you lose both the type safety and the automatic refactoring.
272276:::
273277274278Vitest has comprehensive guides for specific mocking scenarios:
+19-10
docs/guide/learn/setup-teardown.md
···121121import { afterAll, afterEach, beforeAll, beforeEach, describe, test } from 'vitest'
122122123123beforeAll(() => console.log('1 - beforeAll'))
124124-afterAll(() => console.log('6 - afterAll'))
124124+afterAll(() => console.log('8 - afterAll'))
125125beforeEach(() => console.log('2 - beforeEach'))
126126-afterEach(() => console.log('4 - afterEach'))
126126+afterEach(() => console.log('5 - afterEach'))
127127128128describe('suite', () => {
129129 beforeEach(() => console.log('3 - inner beforeEach'))
130130- afterEach(() => console.log('3.5 - inner afterEach'))
130130+ afterEach(() => console.log('4 - inner afterEach'))
131131+132132+ test('first test', () => {
133133+ console.log(' first test')
134134+ })
131135132132- test('example', () => {
133133- console.log(' test')
136136+ test('second test', () => {
137137+ console.log(' second test')
134138 })
135139})
136140```
···1411451 - beforeAll
1421462 - beforeEach
1431473 - inner beforeEach
144144- test
145145-3.5 - inner afterEach
146146-4 - afterEach
147147-6 - afterAll
148148+ first test
149149+4 - inner afterEach
150150+5 - afterEach
151151+2 - beforeEach
152152+3 - inner beforeEach
153153+ second test
154154+4 - inner afterEach
155155+5 - afterEach
156156+8 - afterAll
148157```
149158150150-Notice the pattern: outer `beforeEach` runs first (setting up the broadest context), then inner `beforeEach` runs (narrowing the context). After the test, the order reverses: inner `afterEach` cleans up the narrow context first, then outer `afterEach` handles the broader cleanup.
159159+Notice the pattern: `beforeAll` and `afterAll` run once for the entire suite, while `beforeEach` and `afterEach` repeat for every test. Within each test, outer `beforeEach` runs first (setting up the broadest context), then inner `beforeEach` runs (narrowing the context). After the test, the order reverses: inner `afterEach` cleans up the narrow context first, then outer `afterEach` handles the broader cleanup.
151160152161## Cleanup with `onTestFinished`
153162
+35
docs/guide/learn/snapshots.md
···89899090Inline snapshots are great for small, focused values. For large outputs (like a full HTML page), external snapshots or file snapshots are a better fit.
91919292+::: tip
9393+Unlike external snapshots, inline snapshots don't create separate `.snap` files. The expected value is stored directly in your test file as the argument to `toMatchInlineSnapshot()`, so there's nothing extra to commit.
9494+:::
9595+9296## Updating Snapshots
93979498When you intentionally change the output of your code, existing snapshots will be outdated and the tests will fail. This is by design; it's the whole point of snapshot testing. But once you've verified that the new output is correct, you need to update the snapshots.
···135139On the other hand, snapshots are not always the best tool. If the output changes frequently (for instance, it includes timestamps or random IDs), you'll spend more time updating snapshots than they save you. And if you only care about one or two specific fields, a targeted assertion like [`toMatchObject`](/api/expect#tomatchobject) or [`toHaveProperty`](/api/expect#tohaveproperty) expresses your intent more clearly than a snapshot that captures everything.
136140137141The general rule: use snapshots when you want to protect against *any* change in the output, and use targeted assertions when you only care about *specific* properties.
142142+143143+## Handling Dynamic Values
144144+145145+If your output includes values that change every run (like timestamps or IDs), you can use property matchers to pin the structure while ignoring volatile fields. Pass an object with asymmetric matchers as the first argument to `toMatchSnapshot()` or `toMatchInlineSnapshot()`:
146146+147147+```js
148148+test('user snapshot with dynamic fields', () => {
149149+ const user = createUser('Alice')
150150+151151+ expect(user).toMatchSnapshot({
152152+ id: expect.any(Number),
153153+ createdAt: expect.any(Date),
154154+ })
155155+})
156156+```
157157+158158+The `id` and `createdAt` fields are checked against the matchers (any number, any date) instead of being compared to a stored value. All other fields are snapshotted as usual.
159159+160160+## Error Snapshots
161161+162162+A common use of inline snapshots is capturing error messages. [`toThrowErrorMatchingInlineSnapshot`](/api/expect#tothrowerrormatchinginlinesnapshot) combines `toThrow` with `toMatchInlineSnapshot` so you can snapshot the error message without a separate `.snap` file:
163163+164164+```js
165165+test('throws on invalid input', () => {
166166+ expect(() => parse('')).toThrowErrorMatchingInlineSnapshot(
167167+ `[Error: Unexpected end of input at position 0]`
168168+ )
169169+})
170170+```
171171+172172+This is especially handy for verifying that error messages are clear and don't accidentally change. Like other inline snapshots, Vitest fills in the string on the first run and updates it when you press `u`.
138173139174::: tip
140175For custom snapshot serializers, snapshot matchers, and advanced configuration, see the [Snapshot](/guide/snapshot) guide.
+5-1
docs/guide/learn/testing-in-practice.md
···3838})
39394040test('formats EUR prices', () => {
4141- expect(formatPrice(10, 'EUR')).toMatchInlineSnapshot(`"€10.00"`)
4141+ expect(formatPrice(10, 'EUR')).toBe('€10.00')
4242})
43434444test('handles zero', () => {
···429429430430::: tip
431431Notice that we create a fresh `createTodoList()` in every test. This keeps tests independent, which means they can run in any order without affecting each other. If you find yourself repeating the same setup in every test, that's a good candidate for [`beforeEach`](/api/hooks#beforeeach) or a [`test.extend`](/guide/test-context#extend-test-context) fixture.
432432+:::
433433+434434+::: details What about `nextId`?
435435+The `nextId` counter at the top of the module is shared across all calls to `createTodoList()`, including across tests. This means IDs aren't predictable: one test might get IDs 1 and 2, while another gets 3 and 4 depending on execution order. This works fine here because the tests only check *relative* uniqueness (`first.id !== second.id`), not specific ID values. If a test asserted `expect(todo.id).toBe(1)`, it would break depending on which tests ran before it. When you have shared module-level state like this, make sure your tests don't depend on its specific value.
432436:::
433437434438---
+79-13
docs/guide/learn/writing-tests.md
···86868787If the default patterns don't work for your project, you can customize which files are included with the [`include`](/config/include) and [`exclude`](/config/exclude) config options.
88888989+## Testing TypeScript
9090+9191+Because Vitest runs on top of Vite, TypeScript works out of the box. There's no extra compiler to install, no `ts-jest` to configure, and no separate build step for your tests. Just name your test file `.test.ts` instead of `.test.js` and start writing:
9292+9393+```ts
9494+import { expect, test } from 'vitest'
9595+9696+interface User {
9797+ name: string
9898+ age: number
9999+}
100100+101101+function createUser(name: string, age: number): User {
102102+ return { name, age }
103103+}
104104+105105+test('creates a user with the correct fields', () => {
106106+ const user = createUser('Alice', 30)
107107+108108+ expect(user).toEqual({ name: 'Alice', age: 30 })
109109+ expect(user.name).toBe('Alice')
110110+})
111111+```
112112+113113+You can import your production types, use generics, and write typed test utilities exactly as you would in the rest of your codebase. Vite transforms TypeScript on the fly, so tests start fast even in large projects.
114114+115115+::: tip
116116+Vitest transforms TypeScript for execution but does **not** type-check your tests during the test run. This is the same trade-off Vite makes for speed: you get fast feedback in the terminal, and run `tsc` or `vitest typecheck` separately when you want full type checking. See the [Testing Types](/guide/testing-types) guide for more details.
117117+:::
118118+89119## Reading Test Output
9012091121When you run `vitest` and only a single test file matches, the output is expanded into a tree structure showing `describe` groups and individual tests along with their duration:
···130160131161These modifiers are great for quick, local changes while developing. For more permanent ways to filter tests (by filename, line number, or tags), see the [Test Filtering](/guide/filtering) guide.
132162163163+## Parameterized Tests
164164+165165+When you have several test cases that only differ in their inputs and expected outputs, writing a separate `test` for each one gets repetitive. [`test.for`](/api/test#test-for) lets you define the cases as data and run the same test logic for all of them:
166166+167167+```js
168168+import { expect, test } from 'vitest'
169169+170170+test.for([
171171+ [1, 1, 2],
172172+ [1, 2, 3],
173173+ [2, 1, 3],
174174+])('add(%i, %i) -> %i', ([a, b, expected]) => {
175175+ expect(a + b).toBe(expected)
176176+})
177177+```
178178+179179+The placeholders `%i`, `%s`, and `%f` in the test name are replaced with the corresponding values from each row, so the output shows `add(1, 1) -> 2`, `add(1, 2) -> 3`, and so on.
180180+181181+If your cases have more than two or three values, passing objects is more readable. Use `$property` in the name to interpolate fields:
182182+183183+```js
184184+test.for([
185185+ { a: 1, b: 1, expected: 2 },
186186+ { a: 1, b: 2, expected: 3 },
187187+ { a: 2, b: 1, expected: 3 },
188188+])('add($a, $b) -> $expected', ({ a, b, expected }) => {
189189+ expect(a + b).toBe(expected)
190190+})
191191+```
192192+193193+The second argument to the test function is the [Test Context](/guide/test-context), which gives you access to fixtures, per-test `expect`, and other utilities. This is especially useful with [`test.concurrent`](/api/test#concurrent), where concurrent tests run in parallel and the global `expect` can't reliably associate a snapshot with the right test. The context-scoped `expect` solves this:
194194+195195+```js
196196+test.concurrent.for([
197197+ [1, 1],
198198+ [1, 2],
199199+ [2, 1],
200200+])('add(%i, %i)', ([a, b], { expect }) => {
201201+ expect(a + b).toMatchSnapshot()
202202+})
203203+```
204204+205205+[`describe.for`](/api/describe#describe-for) works the same way but creates a suite for each set of parameters, which is useful when multiple tests share the same parameterized setup.
206206+207207+::: tip
208208+Vitest also provides [`test.each`](/api/test#each), which you may recognize from Jest. It works similarly but spreads array arguments instead of passing them as a single value, and doesn't provide access to the Test Context. It exists mainly for Jest compatibility. Prefer `test.for` in new code.
209209+:::
210210+133211## Using Global Imports
134212135213By default, you import `test`, `expect`, `describe`, and other functions from `vitest` at the top of every test file. If you'd rather use them as globals without importing (similar to how Jest works), you can enable the [`globals`](/config/globals) option in your config:
···160238161239Vitest runs all test files **in parallel** by default, using [child processes](/config/pool). Each test file runs in its own isolated context, so your test files don't share state with each other. This prevents tests in different files from accidentally interfering.
162240163163-Tests **within** a single file run sequentially by default, which is usually what you want since tests in the same file often share setup code. If your tests are truly independent, you can opt into running them concurrently with [`test.concurrent`](/api/test#concurrent) to speed things up:
164164-165165-```js
166166-test.concurrent('first concurrent test', async () => {
167167- // runs in parallel with the next test
168168-})
169169-170170-test.concurrent('second concurrent test', async () => {
171171- // runs in parallel with the previous test
172172-})
173173-```
174174-175175-See the [Parallelism](/guide/parallelism) guide for more details on controlling test execution.
241241+Tests **within** a single file run sequentially by default, which is usually what you want since tests in the same file often share setup code. If your tests are truly independent, you can opt into running them concurrently with [`test.concurrent`](/api/test#concurrent) to speed things up. See the [Parallelism](/guide/parallelism) guide for more details on controlling test execution.