Skip to content

Commit 9e412de

Browse files
authored
fix!: rewrite spying implementation to make module mocking more intuitive (#8363)
1 parent 459efba commit 9e412de

34 files changed

+3490
-1173
lines changed

docs/.vitepress/config.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,41 @@ function guide(): DefaultTheme.SidebarItem[] {
502502
{
503503
text: 'Mocking',
504504
link: '/guide/mocking',
505+
collapsed: true,
506+
items: [
507+
{
508+
text: 'Mocking Dates',
509+
link: '/guide/mocking#dates',
510+
},
511+
{
512+
text: 'Mocking Functions',
513+
link: '/guide/mocking#functions',
514+
},
515+
{
516+
text: 'Mocking Globals',
517+
link: '/guide/mocking#globals',
518+
},
519+
{
520+
text: 'Mocking Modules',
521+
link: '/guide/mocking-modules',
522+
},
523+
{
524+
text: 'Mocking File System',
525+
link: '/guide/mocking#file-system',
526+
},
527+
{
528+
text: 'Mocking Requests',
529+
link: '/guide/mocking#requests',
530+
},
531+
{
532+
text: 'Mocking Timers',
533+
link: '/guide/mocking#timers',
534+
},
535+
{
536+
text: 'Mocking Classes',
537+
link: '/guide/mocking#classes',
538+
},
539+
],
505540
},
506541
{
507542
text: 'Parallelism',
@@ -586,7 +621,7 @@ function api(): DefaultTheme.SidebarItem[] {
586621
link: '/api/',
587622
},
588623
{
589-
text: 'Mock Functions',
624+
text: 'Mocks',
590625
link: '/api/mock',
591626
},
592627
{

docs/api/mock.md

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Mock Functions
1+
# Mocks
22

3-
You can create a mock function to track its execution with `vi.fn` method. If you want to track a method on an already created object, you can use `vi.spyOn` method:
3+
You can create a mock function or a class to track its execution with the `vi.fn` method. If you want to track a property on an already created object, you can use the `vi.spyOn` method:
44

55
```js
66
import { vi } from 'vitest'
@@ -18,7 +18,7 @@ market.getApples()
1818
getApplesSpy.mock.calls.length === 1
1919
```
2020

21-
You should use mock assertions (e.g., [`toHaveBeenCalled`](/api/expect#tohavebeencalled)) on [`expect`](/api/expect) to assert mock result. This API reference describes available properties and methods to manipulate mock behavior.
21+
You should use mock assertions (e.g., [`toHaveBeenCalled`](/api/expect#tohavebeencalled)) on [`expect`](/api/expect) to assert mock results. This API reference describes available properties and methods to manipulate mock behavior.
2222

2323
::: tip
2424
The custom function implementation in the types below is marked with a generic `<T>`.
@@ -30,7 +30,7 @@ The custom function implementation in the types below is marked with a generic `
3030
function getMockImplementation(): T | undefined
3131
```
3232

33-
Returns current mock implementation if there is one.
33+
Returns the current mock implementation if there is one.
3434

3535
If the mock was created with [`vi.fn`](/api/vi#vi-fn), it will use the provided method as the mock implementation.
3636

@@ -42,12 +42,12 @@ If the mock was created with [`vi.spyOn`](/api/vi#vi-spyon), it will return `und
4242
function getMockName(): string
4343
```
4444

45-
Use it to return the name assigned to the mock with the `.mockName(name)` method. By default, it will return `vi.fn()`.
45+
Use it to return the name assigned to the mock with the `.mockName(name)` method. By default, `vi.fn()` mocks will return `'vi.fn()'`, while spies created with `vi.spyOn` will keep the original name.
4646

4747
## mockClear
4848

4949
```ts
50-
function mockClear(): MockInstance<T>
50+
function mockClear(): Mock<T>
5151
```
5252

5353
Clears all information about every call. After calling it, all properties on `.mock` will return to their initial state. This method does not reset implementations. It is useful for cleaning up mocks between different assertions.
@@ -72,15 +72,15 @@ To automatically call this method before each test, enable the [`clearMocks`](/c
7272
## mockName
7373

7474
```ts
75-
function mockName(name: string): MockInstance<T>
75+
function mockName(name: string): Mock<T>
7676
```
7777

7878
Sets the internal mock name. This is useful for identifying the mock when an assertion fails.
7979

8080
## mockImplementation
8181

8282
```ts
83-
function mockImplementation(fn: T): MockInstance<T>
83+
function mockImplementation(fn: T): Mock<T>
8484
```
8585

8686
Accepts a function to be used as the mock implementation. TypeScript expects the arguments and return type to match those of the original function.
@@ -102,7 +102,7 @@ mockFn.mock.calls[1][0] === 1 // true
102102
## mockImplementationOnce
103103

104104
```ts
105-
function mockImplementationOnce(fn: T): MockInstance<T>
105+
function mockImplementationOnce(fn: T): Mock<T>
106106
```
107107

108108
Accepts a function to be used as the mock implementation. TypeScript expects the arguments and return type to match those of the original function. This method can be chained to produce different results for multiple function calls.
@@ -135,11 +135,11 @@ console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn())
135135
function withImplementation(
136136
fn: T,
137137
cb: () => void
138-
): MockInstance<T>
138+
): Mock<T>
139139
function withImplementation(
140140
fn: T,
141141
cb: () => Promise<void>
142-
): Promise<MockInstance<T>>
142+
): Promise<Mock<T>>
143143
```
144144

145145
Overrides the original mock implementation temporarily while the callback is being executed.
@@ -177,10 +177,10 @@ Note that this method takes precedence over the [`mockImplementationOnce`](#mock
177177
## mockRejectedValue
178178

179179
```ts
180-
function mockRejectedValue(value: unknown): MockInstance<T>
180+
function mockRejectedValue(value: unknown): Mock<T>
181181
```
182182

183-
Accepts an error that will be rejected when async function is called.
183+
Accepts an error that will be rejected when an async function is called.
184184

185185
```ts
186186
const asyncMock = vi.fn().mockRejectedValue(new Error('Async error'))
@@ -191,7 +191,7 @@ await asyncMock() // throws Error<'Async error'>
191191
## mockRejectedValueOnce
192192

193193
```ts
194-
function mockRejectedValueOnce(value: unknown): MockInstance<T>
194+
function mockRejectedValueOnce(value: unknown): Mock<T>
195195
```
196196

197197
Accepts a value that will be rejected during the next function call. If chained, each consecutive call will reject the specified value.
@@ -209,14 +209,13 @@ await asyncMock() // throws Error<'Async error'>
209209
## mockReset
210210

211211
```ts
212-
function mockReset(): MockInstance<T>
212+
function mockReset(): Mock<T>
213213
```
214214

215-
Does what [`mockClear`](#mockClear) does and resets inner implementation to the original function.
216-
This also resets all "once" implementations.
215+
Does what [`mockClear`](#mockClear) does and resets the mock implementation. This also resets all "once" implementations.
217216

218-
Note that resetting a mock from `vi.fn()` will set implementation to an empty function that returns `undefined`.
219-
resetting a mock from `vi.fn(impl)` will restore implementation to `impl`.
217+
Note that resetting a mock from `vi.fn()` will set the implementation to an empty function that returns `undefined`.
218+
Resetting a mock from `vi.fn(impl)` will reset the implementation to `impl`.
220219

221220
This is useful when you want to reset a mock to its original state.
222221

@@ -241,13 +240,12 @@ To automatically call this method before each test, enable the [`mockReset`](/co
241240
## mockRestore
242241

243242
```ts
244-
function mockRestore(): MockInstance<T>
243+
function mockRestore(): Mock<T>
245244
```
246245

247-
Does what [`mockReset`](#mockReset) does and restores original descriptors of spied-on objects.
246+
Does what [`mockReset`](#mockreset) does and restores the original descriptors of spied-on objects, if the mock was created with [`vi.spyOn`](/api/vi#vi-spyon).
248247

249-
Note that restoring a mock from `vi.fn()` will set implementation to an empty function that returns `undefined`.
250-
Restoring a mock from `vi.fn(impl)` will restore implementation to `impl`.
248+
`mockRestore` on a `vi.fn()` mock is identical to [`mockReset`](#mockreset).
251249

252250
```ts
253251
const person = {
@@ -270,7 +268,7 @@ To automatically call this method before each test, enable the [`restoreMocks`](
270268
## mockResolvedValue
271269

272270
```ts
273-
function mockResolvedValue(value: Awaited<ReturnType<T>>): MockInstance<T>
271+
function mockResolvedValue(value: Awaited<ReturnType<T>>): Mock<T>
274272
```
275273

276274
Accepts a value that will be resolved when the async function is called. TypeScript will only accept values that match the return type of the original function.
@@ -284,7 +282,7 @@ await asyncMock() // 42
284282
## mockResolvedValueOnce
285283

286284
```ts
287-
function mockResolvedValueOnce(value: Awaited<ReturnType<T>>): MockInstance<T>
285+
function mockResolvedValueOnce(value: Awaited<ReturnType<T>>): Mock<T>
288286
```
289287

290288
Accepts a value that will be resolved during the next function call. TypeScript will only accept values that match the return type of the original function. If chained, each consecutive call will resolve the specified value.
@@ -305,7 +303,7 @@ await asyncMock() // default
305303
## mockReturnThis
306304

307305
```ts
308-
function mockReturnThis(): MockInstance<T>
306+
function mockReturnThis(): Mock<T>
309307
```
310308

311309
Use this if you need to return the `this` context from the method without invoking the actual implementation. This is a shorthand for:
@@ -319,7 +317,7 @@ spy.mockImplementation(function () {
319317
## mockReturnValue
320318

321319
```ts
322-
function mockReturnValue(value: ReturnType<T>): MockInstance<T>
320+
function mockReturnValue(value: ReturnType<T>): Mock<T>
323321
```
324322

325323
Accepts a value that will be returned whenever the mock function is called. TypeScript will only accept values that match the return type of the original function.
@@ -335,7 +333,7 @@ mock() // 43
335333
## mockReturnValueOnce
336334

337335
```ts
338-
function mockReturnValueOnce(value: ReturnType<T>): MockInstance<T>
336+
function mockReturnValueOnce(value: ReturnType<T>): Mock<T>
339337
```
340338

341339
Accepts a value that will be returned whenever the mock function is called. TypeScript will only accept values that match the return type of the original function.
@@ -379,7 +377,7 @@ fn.mock.calls === [
379377
const lastCall: Parameters<T> | undefined
380378
```
381379

382-
This contains the arguments of the last call. If mock wasn't called, it will return `undefined`.
380+
This contains the arguments of the last call. If the mock wasn't called, it will return `undefined`.
383381

384382
## mock.results
385383

@@ -388,7 +386,7 @@ interface MockResultReturn<T> {
388386
type: 'return'
389387
/**
390388
* The value that was returned from the function.
391-
* If function returned a Promise, then this will be a resolved value.
389+
* If the function returned a Promise, then this will be a resolved value.
392390
*/
393391
value: T
394392
}
@@ -418,6 +416,7 @@ This is an array containing all values that were `returned` from the function. O
418416

419417
- `'return'` - function returned without throwing.
420418
- `'throw'` - function threw a value.
419+
- `'incomplete'` - the function did not finish running yet.
421420

422421
The `value` property contains the returned value or thrown error. If the function returned a `Promise`, then `result` will always be `'return'` even if the promise was rejected.
423422

@@ -450,6 +449,11 @@ fn.mock.results === [
450449
## mock.settledResults
451450

452451
```ts
452+
interface MockSettledResultIncomplete {
453+
type: 'incomplete'
454+
value: undefined
455+
}
456+
453457
interface MockSettledResultFulfilled<T> {
454458
type: 'fulfilled'
455459
value: T
@@ -463,20 +467,28 @@ interface MockSettledResultRejected {
463467
export type MockSettledResult<T>
464468
= | MockSettledResultFulfilled<T>
465469
| MockSettledResultRejected
470+
| MockSettledResultIncomplete
466471
467472
const settledResults: MockSettledResult<Awaited<ReturnType<T>>>[]
468473
```
469474

470-
An array containing all values that were `resolved` or `rejected` from the function.
475+
An array containing all values that were resolved or rejected by the function.
476+
477+
If the function returned non-promise values, the `value` will be kept as is, but the `type` will still says `fulfilled` or `rejected`.
471478

472-
This array will be empty if the function was never resolved or rejected.
479+
Until the value is resolved or rejected, the `settledResult` type will be `incomplete`.
473480

474481
```js
475482
const fn = vi.fn().mockResolvedValueOnce('result')
476483
477484
const result = fn()
478485
479-
fn.mock.settledResults === []
486+
fn.mock.settledResults === [
487+
{
488+
type: 'incomplete',
489+
value: undefined,
490+
},
491+
]
480492
481493
await result
482494
@@ -533,10 +545,10 @@ fn.mock.contexts[1] === context
533545
const instances: ReturnType<T>[]
534546
```
535547

536-
This property is an array containing all instances that were created when the mock was called with the `new` keyword. Note that this is an actual context (`this`) of the function, not a return value.
548+
This property is an array containing all instances that were created when the mock was called with the `new` keyword. Note that this is the actual context (`this`) of the function, not a return value.
537549

538550
::: warning
539-
If mock was instantiated with `new MyClass()`, then `mock.instances` will be an array with one value:
551+
If the mock was instantiated with `new MyClass()`, then `mock.instances` will be an array with one value:
540552

541553
```js
542554
const MyClass = vi.fn()
@@ -545,7 +557,7 @@ const a = new MyClass()
545557
MyClass.mock.instances[0] === a
546558
```
547559

548-
If you return a value from constructor, it will not be in `instances` array, but instead inside `results`:
560+
If you return a value from the constructor, it will not be in the `instances` array, but instead inside `results`:
549561

550562
```js
551563
const Spy = vi.fn(() => ({ method: vi.fn() }))

0 commit comments

Comments
 (0)