[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

docs: update mocking guide (#8514)

authored by

Vladimir and committed by
GitHub
(Aug 31, 2025, 12:47 PM +0200) 2b9b3431 bbb3514f

+977 -1003
+9 -9
docs/.vitepress/config.ts
··· 508 508 items: [ 509 509 { 510 510 text: 'Mocking Dates', 511 - link: '/guide/mocking#dates', 511 + link: '/guide/mocking/dates', 512 512 }, 513 513 { 514 514 text: 'Mocking Functions', 515 - link: '/guide/mocking#functions', 515 + link: '/guide/mocking/functions', 516 516 }, 517 517 { 518 518 text: 'Mocking Globals', 519 - link: '/guide/mocking#globals', 519 + link: '/guide/mocking/globals', 520 520 }, 521 521 { 522 522 text: 'Mocking Modules', 523 - link: '/guide/mocking-modules', 523 + link: '/guide/mocking/modules', 524 524 }, 525 525 { 526 - text: 'Mocking File System', 527 - link: '/guide/mocking#file-system', 526 + text: 'Mocking the File System', 527 + link: '/guide/mocking/file-system', 528 528 }, 529 529 { 530 530 text: 'Mocking Requests', 531 - link: '/guide/mocking#requests', 531 + link: '/guide/mocking/requests', 532 532 }, 533 533 { 534 534 text: 'Mocking Timers', 535 - link: '/guide/mocking#timers', 535 + link: '/guide/mocking/timers', 536 536 }, 537 537 { 538 538 text: 'Mocking Classes', 539 - link: '/guide/mocking#classes', 539 + link: '/guide/mocking/classes', 540 540 }, 541 541 ], 542 542 },
+7 -7
docs/api/vi.md
··· 12 12 13 13 ## Mock Modules 14 14 15 - This section describes the API that you can use when [mocking a module](/guide/mocking#modules). Beware that Vitest doesn't support mocking modules imported using `require()`. 15 + This section describes the API that you can use when [mocking a module](/guide/mocking/modules). Beware that Vitest doesn't support mocking modules imported using `require()`. 16 16 17 17 ### vi.mock 18 18 ··· 171 171 Beware that if you don't call `vi.mock`, modules **are not** mocked automatically. To replicate Jest's automocking behaviour, you can call `vi.mock` for each required module inside [`setupFiles`](/config/#setupfiles). 172 172 ::: 173 173 174 - If there is no `__mocks__` folder or a factory provided, Vitest will import the original module and auto-mock all its exports. For the rules applied, see [algorithm](/guide/mocking#automocking-algorithm). 174 + If there is no `__mocks__` folder or a factory provided, Vitest will import the original module and auto-mock all its exports. For the rules applied, see [algorithm](/guide/mocking/modules#automocking-algorithm). 175 175 176 176 ### vi.doMock 177 177 ··· 295 295 function importMock<T>(path: string): Promise<MaybeMockedDeep<T>> 296 296 ``` 297 297 298 - Imports a module with all of its properties (including nested properties) mocked. Follows the same rules that [`vi.mock`](#vi-mock) does. For the rules applied, see [algorithm](/guide/mocking#automocking-algorithm). 298 + Imports a module with all of its properties (including nested properties) mocked. Follows the same rules that [`vi.mock`](#vi-mock) does. For the rules applied, see [algorithm](/guide/mocking/modules#automocking-algorithm). 299 299 300 300 ### vi.unmock 301 301 ··· 420 420 function fn(fn?: Procedure | Constructable): Mock 421 421 ``` 422 422 423 - Creates a spy on a function, though can be initiated without one. Every time a function is invoked, it stores its call arguments, returns, and instances. Also, you can manipulate its behavior with [methods](/api/mock). 424 - If no function is given, mock will return `undefined`, when invoked. 423 + Creates a spy on a function, but can also be initiated without one. Every time a function is invoked, it stores its call arguments, returns, and instances. Additionally, you can manipulate its behavior with [methods](/api/mock). 424 + If no function is given, mock will return `undefined` when invoked. 425 425 426 426 ```ts 427 427 const getApples = vi.fn(() => 0) ··· 524 524 After the mock was restored, you can spy on it again. 525 525 526 526 ::: warning 527 - This method also does not affect mocks created during [automocking](/guide/mocking-modules#mocking-a-module). 527 + This method also does not affect mocks created during [automocking](/guide/mocking/modules#mocking-a-module). 528 528 529 529 Note that unlike [`mock.mockRestore`](/api/mock#mockrestore), `vi.restoreAllMocks` will not clear mock history or reset the mock implementation 530 530 ::: ··· 768 768 769 769 ## Fake Timers 770 770 771 - This sections describes how to work with [fake timers](/guide/mocking#timers). 771 + This sections describes how to work with [fake timers](/guide/mocking/timers). 772 772 773 773 ### vi.advanceTimersByTime 774 774
-7
docs/guide/cli-generated.md
··· 341 341 342 342 Provider used to run browser tests. Some browsers are only available for specific providers. Can be "webdriverio", "playwright", "preview", or the path to a custom provider. Visit [`browser.provider`](https://vitest.dev/guide/browser/config.html#browser-provider) for more information (default: `"preview"`) 343 343 344 - ### browser.providerOptions 345 - 346 - - **CLI:** `--browser.providerOptions <options>` 347 - - **Config:** [browser.providerOptions](/guide/browser/config#browser-provideroptions) 348 - 349 - Options that are passed down to a browser provider. Visit [`browser.providerOptions`](https://vitest.dev/config/#browser-provideroptions) for more information 350 - 351 344 ### browser.isolate 352 345 353 346 - **CLI:** `--browser.isolate`
-410
docs/guide/mocking-modules.md
··· 1 - # Mocking Modules 2 - 3 - ## Defining a Module 4 - 5 - Before mocking a "module", we should define what it is. In Vitest context, the "module" is a file that exports something. Using [plugins](https://vite.dev/guide/api-plugin.html), any file can be turned into a JavaScript module. The "module object" is a namespace object that holds dynamic references to exported identifiers. Simply put, it's an object with exported methods and properties. In this example, `example.js` is a module that exports `method` and `variable`: 6 - 7 - ```js [example.js] 8 - export function answer() { 9 - // ... 10 - return 42 11 - } 12 - 13 - export const variable = 'example' 14 - ``` 15 - 16 - The `exampleObject` here is a module object: 17 - 18 - ```js [example.test.js] 19 - import * as exampleObject from './example.js' 20 - ``` 21 - 22 - The `exampleObject` will always exist even if you imported the example using named imports: 23 - 24 - ```js [example.test.js] 25 - import { answer, variable } from './example.js' 26 - ``` 27 - 28 - You can only reference `exampleObject` outside the example module itself. For example, in a test. 29 - 30 - ## Mocking a Module 31 - 32 - For the purpose of this guide, let's introduce some definitions. 33 - 34 - - **Mocked module** is a module that was completely replaced with another one. 35 - - **Spied module** is a mocked module, but its exported methods keep the original implementation. They can also be tracked. 36 - - **Mocked export** is a module export, which invocations can be tracked. 37 - - **Spied export** is a mocked export. 38 - 39 - To mock a module completely, you can use the [`vi.mock` API](/api/vi#vi-mock). You can define a new module dynamically by providing a factory that returns a new module as a second argument: 40 - 41 - ```ts 42 - import { vi } from 'vitest' 43 - 44 - // The ./example.js module will be replaced with 45 - // the result of a factory function, and the 46 - // original ./example.js module will never be called 47 - vi.mock(import('./example.js'), () => { 48 - return { 49 - answer() { 50 - // ... 51 - return 42 52 - }, 53 - variable: 'mock', 54 - } 55 - }) 56 - ``` 57 - 58 - ::: tip 59 - Remember that you can call `vi.mock` in a [setup file](/config/#setupfiles) to apply the module mock in every test file automatically. 60 - ::: 61 - 62 - ::: tip 63 - Note the usage of dynamic import: `import('./example.ts')`. Vitest will strip it before the code is executed, but it allows TypeScript to properly validate the string and type the `importOriginal` method in your IDE or CLI. 64 - ::: 65 - 66 - If your code is trying to access a method that was not returned from this factory, Vitest will throw an error with a helpful message. Note that `answer` is not mocked, i.e. it cannot be tracked. To make it trackable, use `vi.fn()` instead: 67 - 68 - ```ts 69 - import { vi } from 'vitest' 70 - 71 - vi.mock(import('./example.js'), () => { 72 - return { 73 - answer: vi.fn(), 74 - variable: 'mock', 75 - } 76 - }) 77 - ``` 78 - 79 - The factory method accepts an `importOriginal` function that will execute the original module and return its module object: 80 - 81 - ```ts 82 - import { expect, vi } from 'vitest' 83 - import { answer } from './example.js' 84 - 85 - vi.mock(import('./example.js'), async (importOriginal) => { 86 - const originalModule = await importOriginal() 87 - return { 88 - answer: vi.fn(originalModule.answer), 89 - variable: 'mock', 90 - } 91 - }) 92 - 93 - expect(answer()).toBe(42) 94 - 95 - expect(answer).toHaveBeenCalled() 96 - expect(answer).toHaveReturned(42) 97 - ``` 98 - 99 - ::: warning 100 - Note that `importOriginal` is asynchronous and needs to be awaited. 101 - ::: 102 - 103 - In the above example, we provided the original `answer` to the `vi.fn()` call so it can keep calling it while being tracked at the same time. 104 - 105 - If you require the use of `importOriginal`, consider spying on the export directly via another API: `vi.spyOn`. Instead of replacing the whole module, you can spy only on a single exported method. To do that, you need to import the module as a namespace object: 106 - 107 - ```ts 108 - import { expect, vi } from 'vitest' 109 - import * as exampleObject from './example.js' 110 - 111 - const spy = vi.spyOn(exampleObject, 'answer').mockReturnValue(0) 112 - 113 - expect(exampleObject.answer()).toBe(0) 114 - expect(exampleObject.answer).toHaveBeenCalled() 115 - ``` 116 - 117 - ::: danger Browser Mode Support 118 - This will not work in the [Browser Mode](/guide/browser/) because it uses the browser's native ESM support to serve modules. The module namespace object is sealed and can't be reconfigured. To bypass this limitation, Vitest supports `{ spy: true }` option in `vi.mock('./example.js')`. This will automatically spy on every export in the module without replacing them with fake ones. 119 - 120 - ```ts 121 - import { vi } from 'vitest' 122 - import * as exampleObject from './example.js' 123 - 124 - vi.mock('./example.js', { spy: true }) 125 - 126 - vi.mocked(exampleObject.answer).mockReturnValue(0) 127 - ``` 128 - ::: 129 - 130 - ::: warning 131 - You only need to import the module as a namespace object in the file where you are using the `vi.spyOn` utility. If the `answer` is called in another file and is imported there as a named export, Vitest will be able to properly track it as long as the function that called it is called after `vi.spyOn`: 132 - 133 - ```ts [source.js] 134 - import { answer } from './example.js' 135 - 136 - export function question() { 137 - if (answer() === 42) { 138 - return 'Ultimate Question of Life, the Universe, and Everything' 139 - } 140 - 141 - return 'Unknown Question' 142 - } 143 - ``` 144 - ::: 145 - 146 - Note that `vi.spyOn` will only spy on calls that were done after it spied on the method. So, if the function is executed at the top level during an import or it was called before the spying, `vi.spyOn` will not be able to report on it. 147 - 148 - To automatically mock any module before it is imported, you can call `vi.mock` with a path: 149 - 150 - ```ts 151 - import { vi } from 'vitest' 152 - 153 - vi.mock(import('./example.js')) 154 - ``` 155 - 156 - If the file `./__mocks__/example.js` exists, then Vitest will load it instead. Otherwise, Vitest will load the original module and replace everything recursively: 157 - 158 - - All arrays will be empty 159 - - All primitives will stay untouched 160 - - All getters will return `undefined` 161 - - All methods will return `undefined` 162 - - All objects will be deeply cloned 163 - - All instances of classes and their prototypes will be cloned 164 - 165 - To disable this behavior, you can pass down `spy: true` as the second argument: 166 - 167 - ```ts 168 - import { vi } from 'vitest' 169 - 170 - vi.mock(import('./example.js'), { spy: true }) 171 - ``` 172 - 173 - Instead of returning `undefined`, all methods will call the original implementation, but you can still keep track of these calls: 174 - 175 - ```ts 176 - import { expect, vi } from 'vitest' 177 - import { answer } from './example.js' 178 - 179 - vi.mock(import('./example.js'), { spy: true }) 180 - 181 - // calls the original implementation 182 - expect(answer()).toBe(42) 183 - // vitest can still track the invocations 184 - expect(answer).toHaveBeenCalled() 185 - ``` 186 - 187 - One nice thing that mocked modules support is sharing the state between the instance and its prototype. Consider this module: 188 - 189 - ```ts [answer.js] 190 - export class Answer { 191 - constructor(value) { 192 - this._value = value 193 - } 194 - 195 - value() { 196 - return this._value 197 - } 198 - } 199 - ``` 200 - 201 - By mocking it, we can keep track of every invocation of `.value()` even without having access to the instance itself: 202 - 203 - ```ts [answer.test.js] 204 - import { expect, test, vi } from 'vitest' 205 - import { Answer } from './answer.js' 206 - 207 - vi.mock(import('./answer.js'), { spy: true }) 208 - 209 - test('instance inherits the state', () => { 210 - // these invocations could be private inside another function 211 - // that you don't have access to in your test 212 - const answer1 = new Answer(42) 213 - const answer2 = new Answer(0) 214 - 215 - expect(answer1.value()).toBe(42) 216 - expect(answer1.value).toHaveBeenCalled() 217 - // note that different instances have their own states 218 - expect(answer2.value).not.toHaveBeenCalled() 219 - 220 - expect(answer2.value()).toBe(0) 221 - 222 - // but the prototype state accumulates all calls 223 - expect(Answer.prototype.value).toHaveBeenCalledTimes(2) 224 - expect(Answer.prototype.value).toHaveReturned(42) 225 - expect(Answer.prototype.value).toHaveReturned(0) 226 - }) 227 - ``` 228 - 229 - This can be very useful to track calls to instances that are never exposed. 230 - 231 - ## Mocking Non-existing Module 232 - 233 - Vitest supports mocking virtual modules. These modules don't exist on the file system, but your code imports them. For example, this can happen when your development environment is different from production. One common example is mocking `vscode` APIs in your unit tests. 234 - 235 - By default, Vitest will fail transforming files if it cannot find the source of the import. To bypass this, you need to specify it in your config. You can either always redirect the import to a file, or just signal Vite to ignore it and use the `vi.mock` factory to define its exports. 236 - 237 - To redirect the import, use [`test.alias`](/config/#alias) config option: 238 - 239 - ```ts [vitest.config.ts] 240 - import { defineConfig } from 'vitest/config' 241 - import { resolve } from 'node:path' 242 - 243 - export default defineConfig({ 244 - test: { 245 - alias: { 246 - vscode: resolve(import.meta.dirname, './mock/vscode.js'), 247 - }, 248 - }, 249 - }) 250 - ``` 251 - 252 - To mark the module as always resolved, return the same string from `resolveId` hook of a plugin: 253 - 254 - ```ts [vitest.config.ts] 255 - import { defineConfig } from 'vitest/config' 256 - import { resolve } from 'node:path' 257 - 258 - export default defineConfig({ 259 - plugins: [ 260 - { 261 - name: 'virtual-vscode', 262 - resolveId(id) { 263 - if (id === 'vscode') { 264 - return 'vscode' 265 - } 266 - } 267 - } 268 - ] 269 - }) 270 - ``` 271 - 272 - Now you can use `vi.mock` as usual in your tests: 273 - 274 - ```ts 275 - import { vi } from 'vitest' 276 - 277 - vi.mock(import('vscode'), () => { 278 - return { 279 - window: { 280 - createOutputChannel: vi.fn(), 281 - } 282 - } 283 - }) 284 - ``` 285 - 286 - ## How it Works 287 - 288 - Vitest implements different module mocking mechanisms depending on the environment. The only feature they share is the plugin transformer. When Vitest sees that a file has `vi.mock` inside, it will transform every static import into a dynamic one and move the `vi.mock` call to the top of the file. This allows Vitest to register the mock before the import happens without breaking the ESM rule of hoisted imports. 289 - 290 - ::: code-group 291 - ```ts [example.js] 292 - import { answer } from './answer.js' 293 - 294 - vi.mock(import('./answer.js')) 295 - 296 - console.log(answer) 297 - ``` 298 - ```ts [example.transformed.js] 299 - vi.mock('./answer.js') 300 - 301 - const __vitest_module_0__ = await __handle_mock__( 302 - () => import('./answer.js') 303 - ) 304 - // to keep the live binding, we have to access 305 - // the export on the module namespace 306 - console.log(__vitest_module_0__.answer()) 307 - ``` 308 - ::: 309 - 310 - The `__handle_mock__` wrapper just makes sure the mock is resolved before the import is initiated, it doesn't modify the module in any way. 311 - 312 - The module mocking plugins are available in the [`@vitest/mocker` package](https://github.com/vitest-dev/vitest/tree/main/packages/mocker). 313 - 314 - ### JSDOM, happy-dom, Node 315 - 316 - When you run your tests in an emulated environment, Vitest creates a [module runner](https://vite.dev/guide/api-environment-runtimes.html#modulerunner) that can consume Vite code. The module runner is designed in such a way that Vitest can hook into the module evaluation and replace it with the mock, if it was registered. This means that Vitest runs your code in an ESM-like environment, but it doesn't use native ESM mechanism directly. This allows the test runner to bend the rules around ES Modules immutability, allowing users to call `vi.spyOn` on a seemingly ES Module. 317 - 318 - ### Browser Mode 319 - 320 - Vitest uses native ESM in the Browser Mode. This means that we cannot replace the module so easily. Instead, Vitest intercepts the fetch request (via playwright's `page.route` or a Vite plugin API if using `preview` or `webdriverio`) and serves transformed code, if the module was mocked. 321 - 322 - For example, if the module is automocked, Vitest can parse static exports and create a placeholder module: 323 - 324 - ::: code-group 325 - ```ts [answer.js] 326 - export function answer() { 327 - return 42 328 - } 329 - ``` 330 - ```ts [answer.transformed.js] 331 - function answer() { 332 - return 42 333 - } 334 - 335 - const __private_module__ = { 336 - [Symbol.toStringTag]: 'Module', 337 - answer: vi.fn(answer), 338 - } 339 - 340 - export const answer = __private_module__.answer 341 - ``` 342 - ::: 343 - 344 - The example is simplified for brevity, but the concept is unchanged. We can inject a `__private_module__` variable into the module to hold the mocked values. If the user called `vi.mock` with `spy: true`, we pass down the original value; otherwise, we create a simple `vi.fn()` mock. 345 - 346 - If user defined a custom factory, this makes it harder to inject the code, but not impossible. When the mocked file is served, we first resolve the factory in the browser, then pass down the keys back to the server, and use them to create a placeholder module: 347 - 348 - ```ts 349 - const resolvedFactoryKeys = await resolveBrowserFactory(url) 350 - const mockedModule = ` 351 - const __private_module__ = getFactoryReturnValue(${url}) 352 - ${resolvedFactoryKeys.map(key => `export const ${key} = __private_module__["${key}"]`).join('\n')} 353 - ` 354 - ``` 355 - 356 - This module can now be served back to the browser. You can inspect the code in the devtools when you run the tests. 357 - 358 - ## Mocking Modules Pitfalls 359 - 360 - Beware that it is not possible to mock calls to methods that are called inside other methods of the same file. For example, in this code: 361 - 362 - ```ts [foobar.js] 363 - export function foo() { 364 - return 'foo' 365 - } 366 - 367 - export function foobar() { 368 - return `${foo()}bar` 369 - } 370 - ``` 371 - 372 - It is not possible to mock the `foo` method from the outside because it is referenced directly. So this code will have no effect on the `foo` call inside `foobar` (but it will affect the `foo` call in other modules): 373 - 374 - ```ts [foobar.test.ts] 375 - import { vi } from 'vitest' 376 - import * as mod from './foobar.js' 377 - 378 - // this will only affect "foo" outside of the original module 379 - vi.spyOn(mod, 'foo') 380 - vi.mock(import('./foobar.js'), async (importOriginal) => { 381 - return { 382 - ...await importOriginal(), 383 - // this will only affect "foo" outside of the original module 384 - foo: () => 'mocked' 385 - } 386 - }) 387 - ``` 388 - 389 - You can confirm this behavior by providing the implementation to the `foobar` method directly: 390 - 391 - ```ts [foobar.test.js] 392 - import * as mod from './foobar.js' 393 - 394 - vi.spyOn(mod, 'foo') 395 - 396 - // exported foo references mocked method 397 - mod.foobar(mod.foo) 398 - ``` 399 - 400 - ```ts [foobar.js] 401 - export function foo() { 402 - return 'foo' 403 - } 404 - 405 - export function foobar(injectedFoo) { 406 - return injectedFoo === foo // false 407 - } 408 - ``` 409 - 410 - This is the intended behavior, and we do not plan to implement a workaround. Consider refactoring your code into multiple files or use techniques such as [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection). We believe that making the application testable is not the responsibility of the test runner, but of the application architecture.
+17 -567
docs/guide/mocking.md
··· 1 1 --- 2 2 title: Mocking | Guide 3 + outline: false 3 4 --- 4 5 5 6 # Mocking ··· 12 13 13 14 If you are not familiar with `vi.fn`, `vi.mock` or `vi.spyOn` methods, check the [API section](/api/vi) first. 14 15 15 - ## Dates 16 - 17 - Sometimes you need to be in control of the date to ensure consistency when testing. Vitest uses [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers) package for manipulating timers, as well as system date. You can find more about the specific API in detail [here](/api/vi#vi-setsystemtime). 18 - 19 - ### Example 20 - 21 - ```js 22 - import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' 23 - 24 - const businessHours = [9, 17] 25 - 26 - function purchase() { 27 - const currentHour = new Date().getHours() 28 - const [open, close] = businessHours 29 - 30 - if (currentHour > open && currentHour < close) { 31 - return { message: 'Success' } 32 - } 33 - 34 - return { message: 'Error' } 35 - } 36 - 37 - describe('purchasing flow', () => { 38 - beforeEach(() => { 39 - // tell vitest we use mocked time 40 - vi.useFakeTimers() 41 - }) 42 - 43 - afterEach(() => { 44 - // restoring date after each test run 45 - vi.useRealTimers() 46 - }) 47 - 48 - it('allows purchases within business hours', () => { 49 - // set hour within business hours 50 - const date = new Date(2000, 1, 1, 13) 51 - vi.setSystemTime(date) 52 - 53 - // access Date.now() will result in the date set above 54 - expect(purchase()).toEqual({ message: 'Success' }) 55 - }) 56 - 57 - it('disallows purchases outside of business hours', () => { 58 - // set hour outside business hours 59 - const date = new Date(2000, 1, 1, 19) 60 - vi.setSystemTime(date) 61 - 62 - // access Date.now() will result in the date set above 63 - expect(purchase()).toEqual({ message: 'Error' }) 64 - }) 65 - }) 66 - ``` 67 - 68 - ## Functions 69 - 70 - Mocking functions can be split up into two different categories; *spying & mocking*. 71 - 72 - Sometimes all you need is to validate whether or not a specific function has been called (and possibly which arguments were passed). In these cases a spy would be all we need which you can use directly with `vi.spyOn()` ([read more here](/api/vi#vi-spyon)). 73 - 74 - However spies can only help you **spy** on functions, they are not able to alter the implementation of those functions. In the case where we do need to create a fake (or mocked) version of a function we can use `vi.fn()` ([read more here](/api/vi#vi-fn)). 75 - 76 - We use [Tinyspy](https://github.com/tinylibs/tinyspy) as a base for mocking functions, but we have our own wrapper to make it `jest` compatible. Both `vi.fn()` and `vi.spyOn()` share the same methods, however only the return result of `vi.fn()` is callable. 77 - 78 - ### Example 79 - 80 - ```js 81 - import { afterEach, describe, expect, it, vi } from 'vitest' 82 - 83 - const messages = { 84 - items: [ 85 - { message: 'Simple test message', from: 'Testman' }, 86 - // ... 87 - ], 88 - getLatest, // can also be a `getter or setter if supported` 89 - } 90 - 91 - function getLatest(index = messages.items.length - 1) { 92 - return messages.items[index] 93 - } 94 - 95 - describe('reading messages', () => { 96 - afterEach(() => { 97 - vi.restoreAllMocks() 98 - }) 99 - 100 - it('should get the latest message with a spy', () => { 101 - const spy = vi.spyOn(messages, 'getLatest') 102 - expect(spy.getMockName()).toEqual('getLatest') 103 - 104 - expect(messages.getLatest()).toEqual( 105 - messages.items[messages.items.length - 1], 106 - ) 107 - 108 - expect(spy).toHaveBeenCalledTimes(1) 109 - 110 - spy.mockImplementationOnce(() => 'access-restricted') 111 - expect(messages.getLatest()).toEqual('access-restricted') 112 - 113 - expect(spy).toHaveBeenCalledTimes(2) 114 - }) 115 - 116 - it('should get with a mock', () => { 117 - const mock = vi.fn().mockImplementation(getLatest) 118 - 119 - expect(mock()).toEqual(messages.items[messages.items.length - 1]) 120 - expect(mock).toHaveBeenCalledTimes(1) 121 - 122 - mock.mockImplementationOnce(() => 'access-restricted') 123 - expect(mock()).toEqual('access-restricted') 124 - 125 - expect(mock).toHaveBeenCalledTimes(2) 126 - 127 - expect(mock()).toEqual(messages.items[messages.items.length - 1]) 128 - expect(mock).toHaveBeenCalledTimes(3) 129 - }) 130 - }) 131 - ``` 132 - 133 - ### More 134 - 135 - - [Jest's Mock Functions](https://jestjs.io/docs/mock-function-api) 136 - 137 - ## Globals 138 - 139 - You can mock global variables that are not present with `jsdom` or `node` by using [`vi.stubGlobal`](/api/vi#vi-stubglobal) helper. It will put the value of the global variable into a `globalThis` object. 140 - 141 - ```ts 142 - import { vi } from 'vitest' 143 - 144 - const IntersectionObserverMock = vi.fn(() => ({ 145 - disconnect: vi.fn(), 146 - observe: vi.fn(), 147 - takeRecords: vi.fn(), 148 - unobserve: vi.fn(), 149 - })) 150 - 151 - vi.stubGlobal('IntersectionObserver', IntersectionObserverMock) 152 - 153 - // now you can access it as `IntersectionObserver` or `window.IntersectionObserver` 154 - ``` 155 - 156 - ## Modules 157 - 158 - See ["Mocking Modules" guide](/guide/mocking-modules). 159 - 160 - ## File System 161 - 162 - Mocking the file system ensures that the tests do not depend on the actual file system, making the tests more reliable and predictable. This isolation helps in avoiding side effects from previous tests. It allows for testing error conditions and edge cases that might be difficult or impossible to replicate with an actual file system, such as permission issues, disk full scenarios, or read/write errors. 163 - 164 - Vitest doesn't provide any file system mocking API out of the box. You can use `vi.mock` to mock the `fs` module manually, but it's hard to maintain. Instead, we recommend using [`memfs`](https://www.npmjs.com/package/memfs) to do that for you. `memfs` creates an in-memory file system, which simulates file system operations without touching the actual disk. This approach is fast and safe, avoiding any potential side effects on the real file system. 165 - 166 - ### Example 167 - 168 - To automatically redirect every `fs` call to `memfs`, you can create `__mocks__/fs.cjs` and `__mocks__/fs/promises.cjs` files at the root of your project: 169 - 170 - ::: code-group 171 - ```ts [__mocks__/fs.cjs] 172 - // we can also use `import`, but then 173 - // every export should be explicitly defined 174 - 175 - const { fs } = require('memfs') 176 - module.exports = fs 177 - ``` 178 - 179 - ```ts [__mocks__/fs/promises.cjs] 180 - // we can also use `import`, but then 181 - // every export should be explicitly defined 182 - 183 - const { fs } = require('memfs') 184 - module.exports = fs.promises 185 - ``` 186 - ::: 187 - 188 - ```ts [read-hello-world.js] 189 - import { readFileSync } from 'node:fs' 190 - 191 - export function readHelloWorld(path) { 192 - return readFileSync(path, 'utf-8') 193 - } 194 - ``` 195 - 196 - ```ts [hello-world.test.js] 197 - import { beforeEach, expect, it, vi } from 'vitest' 198 - import { fs, vol } from 'memfs' 199 - import { readHelloWorld } from './read-hello-world.js' 200 - 201 - // tell vitest to use fs mock from __mocks__ folder 202 - // this can be done in a setup file if fs should always be mocked 203 - vi.mock('node:fs') 204 - vi.mock('node:fs/promises') 205 - 206 - beforeEach(() => { 207 - // reset the state of in-memory fs 208 - vol.reset() 209 - }) 210 - 211 - it('should return correct text', () => { 212 - const path = '/hello-world.txt' 213 - fs.writeFileSync(path, 'hello world') 214 - 215 - const text = readHelloWorld(path) 216 - expect(text).toBe('hello world') 217 - }) 218 - 219 - it('can return a value multiple times', () => { 220 - // you can use vol.fromJSON to define several files 221 - vol.fromJSON( 222 - { 223 - './dir1/hw.txt': 'hello dir1', 224 - './dir2/hw.txt': 'hello dir2', 225 - }, 226 - // default cwd 227 - '/tmp', 228 - ) 229 - 230 - expect(readHelloWorld('/tmp/dir1/hw.txt')).toBe('hello dir1') 231 - expect(readHelloWorld('/tmp/dir2/hw.txt')).toBe('hello dir2') 232 - }) 233 - ``` 234 - 235 - ## Requests 236 - 237 - Because Vitest runs in Node, mocking network requests is tricky; web APIs are not available, so we need something that will mimic network behavior for us. We recommend [Mock Service Worker](https://mswjs.io/) to accomplish this. It allows you to mock `http`, `WebSocket` and `GraphQL` network requests, and is framework agnostic. 238 - 239 - Mock Service Worker (MSW) works by intercepting the requests your tests make, allowing you to use it without changing any of your application code. In-browser, this uses the [Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API). In Node.js, and for Vitest, it uses the [`@mswjs/interceptors`](https://github.com/mswjs/interceptors) library. To learn more about MSW, read their [introduction](https://mswjs.io/docs/) 240 - 241 - ### Configuration 242 - 243 - You can use it like below in your [setup file](/config/#setupfiles) 244 - 245 - ::: code-group 246 - 247 - ```js [HTTP Setup] 248 - import { afterAll, afterEach, beforeAll } from 'vitest' 249 - import { setupServer } from 'msw/node' 250 - import { http, HttpResponse } from 'msw' 251 - 252 - const posts = [ 253 - { 254 - userId: 1, 255 - id: 1, 256 - title: 'first post title', 257 - body: 'first post body', 258 - }, 259 - // ... 260 - ] 261 - 262 - export const restHandlers = [ 263 - http.get('https://rest-endpoint.example/path/to/posts', () => { 264 - return HttpResponse.json(posts) 265 - }), 266 - ] 267 - 268 - const server = setupServer(...restHandlers) 269 - 270 - // Start server before all tests 271 - beforeAll(() => server.listen({ onUnhandledRequest: 'error' })) 272 - 273 - // Close server after all tests 274 - afterAll(() => server.close()) 275 - 276 - // Reset handlers after each test for test isolation 277 - afterEach(() => server.resetHandlers()) 278 - ``` 279 - 280 - ```js [GraphQL Setup] 281 - import { afterAll, afterEach, beforeAll } from 'vitest' 282 - import { setupServer } from 'msw/node' 283 - import { graphql, HttpResponse } from 'msw' 284 - 285 - const posts = [ 286 - { 287 - userId: 1, 288 - id: 1, 289 - title: 'first post title', 290 - body: 'first post body', 291 - }, 292 - // ... 293 - ] 294 - 295 - const graphqlHandlers = [ 296 - graphql.query('ListPosts', () => { 297 - return HttpResponse.json({ 298 - data: { posts }, 299 - }) 300 - }), 301 - ] 302 - 303 - const server = setupServer(...graphqlHandlers) 304 - 305 - // Start server before all tests 306 - beforeAll(() => server.listen({ onUnhandledRequest: 'error' })) 307 - 308 - // Close server after all tests 309 - afterAll(() => server.close()) 310 - 311 - // Reset handlers after each test for test isolation 312 - afterEach(() => server.resetHandlers()) 313 - ``` 314 - 315 - ```js [WebSocket Setup] 316 - import { afterAll, afterEach, beforeAll } from 'vitest' 317 - import { setupServer } from 'msw/node' 318 - import { ws } from 'msw' 319 - 320 - const chat = ws.link('wss://chat.example.com') 321 - 322 - const wsHandlers = [ 323 - chat.addEventListener('connection', ({ client }) => { 324 - client.addEventListener('message', (event) => { 325 - console.log('Received message from client:', event.data) 326 - // Echo the received message back to the client 327 - client.send(`Server received: ${event.data}`) 328 - }) 329 - }), 330 - ] 331 - 332 - const server = setupServer(...wsHandlers) 333 - 334 - // Start server before all tests 335 - beforeAll(() => server.listen({ onUnhandledRequest: 'error' })) 336 - 337 - // Close server after all tests 338 - afterAll(() => server.close()) 339 - 340 - // Reset handlers after each test for test isolation 341 - afterEach(() => server.resetHandlers()) 342 - ``` 343 - ::: 344 - 345 - > Configuring the server with `onUnhandledRequest: 'error'` ensures that an error is thrown whenever there is a request that does not have a corresponding request handler. 346 - 347 - ### More 348 - There is much more to MSW. You can access cookies and query parameters, define mock error responses, and much more! To see all you can do with MSW, read [their documentation](https://mswjs.io/docs). 349 - 350 - ## Timers 351 - 352 - When we test code that involves timeouts or intervals, instead of having our tests wait it out or timeout, we can speed up our tests by using "fake" timers that mock calls to `setTimeout` and `setInterval`. 353 - 354 - See the [`vi.useFakeTimers` API section](/api/vi#vi-usefaketimers) for a more in depth detailed API description. 355 - 356 - ### Example 357 - 358 - ```js 359 - import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' 360 - 361 - function executeAfterTwoHours(func) { 362 - setTimeout(func, 1000 * 60 * 60 * 2) // 2 hours 363 - } 364 - 365 - function executeEveryMinute(func) { 366 - setInterval(func, 1000 * 60) // 1 minute 367 - } 368 - 369 - const mock = vi.fn(() => console.log('executed')) 370 - 371 - describe('delayed execution', () => { 372 - beforeEach(() => { 373 - vi.useFakeTimers() 374 - }) 375 - afterEach(() => { 376 - vi.restoreAllMocks() 377 - }) 378 - it('should execute the function', () => { 379 - executeAfterTwoHours(mock) 380 - vi.runAllTimers() 381 - expect(mock).toHaveBeenCalledTimes(1) 382 - }) 383 - it('should not execute the function', () => { 384 - executeAfterTwoHours(mock) 385 - // advancing by 2ms won't trigger the func 386 - vi.advanceTimersByTime(2) 387 - expect(mock).not.toHaveBeenCalled() 388 - }) 389 - it('should execute every minute', () => { 390 - executeEveryMinute(mock) 391 - vi.advanceTimersToNextTimer() 392 - expect(mock).toHaveBeenCalledTimes(1) 393 - vi.advanceTimersToNextTimer() 394 - expect(mock).toHaveBeenCalledTimes(2) 395 - }) 396 - }) 397 - ``` 398 - 399 - ## Classes 400 - 401 - You can mock an entire class with a single `vi.fn` call. 402 - 403 - ```ts 404 - class Dog { 405 - name: string 406 - 407 - constructor(name: string) { 408 - this.name = name 409 - } 410 - 411 - static getType(): string { 412 - return 'animal' 413 - } 414 - 415 - greet = (): string => { 416 - return `Hi! My name is ${this.name}!` 417 - } 418 - 419 - speak(): string { 420 - return 'bark!' 421 - } 422 - 423 - isHungry() {} 424 - feed() {} 425 - } 426 - ``` 427 - 428 - We can re-create this class with `vi.fn` (or `vi.spyOn().mockImplementation()`): 429 - 430 - ```ts 431 - const Dog = vi.fn(class { 432 - static getType = vi.fn(() => 'mocked animal') 433 - 434 - constructor(name) { 435 - this.name = name 436 - } 437 - 438 - greet = vi.fn(() => `Hi! My name is ${this.name}!`) 439 - speak = vi.fn(() => 'loud bark!') 440 - feed = vi.fn() 441 - }) 442 - ``` 443 - 444 - ::: warning 445 - If a non-primitive is returned from the constructor function, that value will become the result of the new expression. In this case the `[[Prototype]]` may not be correctly bound: 446 - 447 - ```ts 448 - const CorrectDogClass = vi.fn(function (name) { 449 - this.name = name 450 - }) 451 - 452 - const IncorrectDogClass = vi.fn(name => ({ 453 - name 454 - })) 455 - 456 - const Marti = new CorrectDogClass('Marti') 457 - const Newt = new IncorrectDogClass('Newt') 458 - 459 - Marti instanceof CorrectDogClass // ✅ true 460 - Newt instanceof IncorrectDogClass // ❌ false! 461 - ``` 462 - 463 - If you are mocking classes, prefer the class syntax over the function. 464 - ::: 465 - 466 - ::: tip WHEN TO USE? 467 - Generally speaking, you would re-create a class like this inside the module factory if the class is re-exported from another module: 468 - 469 - ```ts 470 - import { Dog } from './dog.js' 471 - 472 - vi.mock(import('./dog.js'), () => { 473 - const Dog = vi.fn(class { 474 - feed = vi.fn() 475 - // ... other mocks 476 - }) 477 - return { Dog } 478 - }) 479 - ``` 480 - 481 - This method can also be used to pass an instance of a class to a function that accepts the same interface: 482 - 483 - ```ts [src/feed.ts] 484 - function feed(dog: Dog) { 485 - // ... 486 - } 487 - ``` 488 - ```ts [tests/dog.test.ts] 489 - import { expect, test, vi } from 'vitest' 490 - import { feed } from '../src/feed.js' 491 - 492 - const Dog = vi.fn(class { 493 - feed = vi.fn() 494 - }) 495 - 496 - test('can feed dogs', () => { 497 - const dogMax = new Dog('Max') 498 - 499 - feed(dogMax) 500 - 501 - expect(dogMax.feed).toHaveBeenCalled() 502 - expect(dogMax.isHungry()).toBe(false) 503 - }) 504 - ``` 505 - ::: 506 - 507 - Now, when we create a new instance of the `Dog` class its `speak` method (alongside `feed` and `greet`) is already mocked: 508 - 509 - ```ts 510 - const Cooper = new Dog('Cooper') 511 - Cooper.speak() // loud bark! 512 - Cooper.greet() // Hi! My name is Cooper! 513 - 514 - // you can use built-in assertions to check the validity of the call 515 - expect(Cooper.speak).toHaveBeenCalled() 516 - expect(Cooper.greet).toHaveBeenCalled() 517 - 518 - const Max = new Dog('Max') 519 - 520 - // methods are not shared between instances if you assigned them directly 521 - expect(Max.speak).not.toHaveBeenCalled() 522 - expect(Max.greet).not.toHaveBeenCalled() 523 - ``` 524 - 525 - We can reassign the return value for a specific instance: 526 - 527 - ```ts 528 - const dog = new Dog('Cooper') 529 - 530 - // "vi.mocked" is a type helper, since 531 - // TypeScript doesn't know that Dog is a mocked class, 532 - // it wraps any function in a Mock<T> type 533 - // without validating if the function is a mock 534 - vi.mocked(dog.speak).mockReturnValue('woof woof') 535 - 536 - dog.speak() // woof woof 537 - ``` 538 - 539 - To mock the property, we can use the `vi.spyOn(dog, 'name', 'get')` method. This makes it possible to use spy assertions on the mocked property: 540 - 541 - ```ts 542 - const dog = new Dog('Cooper') 543 - 544 - const nameSpy = vi.spyOn(dog, 'name', 'get').mockReturnValue('Max') 545 - 546 - expect(dog.name).toBe('Max') 547 - expect(nameSpy).toHaveBeenCalledTimes(1) 548 - ``` 549 - 550 - ::: tip 551 - You can also spy on getters and setters using the same method. 552 - ::: 553 - 554 - ::: danger 555 - Using classes with `vi.fn()` was introduced in Vitest 4. Previously, you had to use `function` and `prototype` inheritence directly. See [v3 guide](https://v3.vitest.dev/guide/mocking.html#classes). 556 - ::: 16 + Vitest has a comprehensive list of guides regarding mocking: 17 + 18 + - [Mocking Classes](/guide/mocking/classes.md) 19 + - [Mocking Dates](/guide/mocking/dates.md) 20 + - [Mocking the File System](/guide/mocking/file-system.md) 21 + - [Mocking Functions](/guide/mocking/functions.md) 22 + - [Mocking Globals](/guide/mocking/globals.md) 23 + - [Mocking Modules](/guide/mocking/modules.md) 24 + - [Mocking Requests](/guide/mocking/requests.md) 25 + - [Mocking Timers](/guide/mocking/timers.md) 26 + 27 + For a simpler and quicker way to get started with mocking, you can check the Cheat Sheet below. 557 28 558 29 ## Cheat Sheet 559 - 560 - :::info 561 - `vi` in the examples below is imported directly from `vitest`. You can also use it globally, if you set `globals` to `true` in your [config](/config/). 562 - ::: 563 30 564 31 I want to… 565 32 ··· 622 89 }) 623 90 return { SomeClass } 624 91 }) 625 - // SomeClass.mock.instances will have SomeClass 626 92 ``` 627 93 628 - 2. Example with `vi.mock` and `.prototype`: 629 - ```ts [example.js] 630 - export class SomeClass {} 631 - ``` 632 - ```ts 633 - import { SomeClass } from './example.js' 634 - 635 - vi.mock(import('./example.js'), () => { 636 - const SomeClass = vi.fn() 637 - SomeClass.prototype.someMethod = vi.fn() 638 - return { SomeClass } 639 - }) 640 - // SomeClass.mock.instances will have SomeClass 641 - ``` 642 - 643 - 3. Example with `vi.spyOn`: 94 + 2. Example with `vi.spyOn`: 644 95 645 96 ```ts 646 97 import * as mod from './example.js' 647 98 648 - const SomeClass = vi.fn() 649 - SomeClass.prototype.someMethod = vi.fn() 650 - 651 - vi.spyOn(mod, 'SomeClass').mockImplementation(SomeClass) 99 + vi.spyOn(mod, 'SomeClass').mockImplementation(class FakeClass { 100 + someMethod = vi.fn() 101 + }) 652 102 ``` 653 103 654 104 ::: warning
+2
docs/guide/browser/index.md
··· 241 241 242 242 ```ts [vitest.config.ts] 243 243 import { defineConfig } from 'vitest/config' 244 + import { playwright } from '@vitest/browser/providers/playwright' 244 245 245 246 export default defineConfig({ 246 247 test: { ··· 268 269 name: 'browser', 269 270 browser: { 270 271 enabled: true, 272 + provider: playwright(), 271 273 instances: [ 272 274 { browser: 'chromium' }, 273 275 ],
+158
docs/guide/mocking/classes.md
··· 1 + # Mocking Classes 2 + 3 + You can mock an entire class with a single [`vi.fn`](/api/vi#fn) call. 4 + 5 + ```ts 6 + class Dog { 7 + name: string 8 + 9 + constructor(name: string) { 10 + this.name = name 11 + } 12 + 13 + static getType(): string { 14 + return 'animal' 15 + } 16 + 17 + greet = (): string => { 18 + return `Hi! My name is ${this.name}!` 19 + } 20 + 21 + speak(): string { 22 + return 'bark!' 23 + } 24 + 25 + isHungry() {} 26 + feed() {} 27 + } 28 + ``` 29 + 30 + We can re-create this class with `vi.fn` (or `vi.spyOn().mockImplementation()`): 31 + 32 + ```ts 33 + const Dog = vi.fn(class { 34 + static getType = vi.fn(() => 'mocked animal') 35 + 36 + constructor(name) { 37 + this.name = name 38 + } 39 + 40 + greet = vi.fn(() => `Hi! My name is ${this.name}!`) 41 + speak = vi.fn(() => 'loud bark!') 42 + feed = vi.fn() 43 + }) 44 + ``` 45 + 46 + ::: warning 47 + If a non-primitive is returned from the constructor function, that value will become the result of the new expression. In this case the `[[Prototype]]` may not be correctly bound: 48 + 49 + ```ts 50 + const CorrectDogClass = vi.fn(function (name) { 51 + this.name = name 52 + }) 53 + 54 + const IncorrectDogClass = vi.fn(name => ({ 55 + name 56 + })) 57 + 58 + const Marti = new CorrectDogClass('Marti') 59 + const Newt = new IncorrectDogClass('Newt') 60 + 61 + Marti instanceof CorrectDogClass // ✅ true 62 + Newt instanceof IncorrectDogClass // ❌ false! 63 + ``` 64 + 65 + If you are mocking classes, prefer the class syntax over the function. 66 + ::: 67 + 68 + ::: tip WHEN TO USE? 69 + Generally speaking, you would re-create a class like this inside the module factory if the class is re-exported from another module: 70 + 71 + ```ts 72 + import { Dog } from './dog.js' 73 + 74 + vi.mock(import('./dog.js'), () => { 75 + const Dog = vi.fn(class { 76 + feed = vi.fn() 77 + // ... other mocks 78 + }) 79 + return { Dog } 80 + }) 81 + ``` 82 + 83 + This method can also be used to pass an instance of a class to a function that accepts the same interface: 84 + 85 + ```ts [src/feed.ts] 86 + function feed(dog: Dog) { 87 + // ... 88 + } 89 + ``` 90 + ```ts [tests/dog.test.ts] 91 + import { expect, test, vi } from 'vitest' 92 + import { feed } from '../src/feed.js' 93 + 94 + const Dog = vi.fn(class { 95 + feed = vi.fn() 96 + }) 97 + 98 + test('can feed dogs', () => { 99 + const dogMax = new Dog('Max') 100 + 101 + feed(dogMax) 102 + 103 + expect(dogMax.feed).toHaveBeenCalled() 104 + expect(dogMax.isHungry()).toBe(false) 105 + }) 106 + ``` 107 + ::: 108 + 109 + Now, when we create a new instance of the `Dog` class its `speak` method (alongside `feed` and `greet`) is already mocked: 110 + 111 + ```ts 112 + const Cooper = new Dog('Cooper') 113 + Cooper.speak() // loud bark! 114 + Cooper.greet() // Hi! My name is Cooper! 115 + 116 + // you can use built-in assertions to check the validity of the call 117 + expect(Cooper.speak).toHaveBeenCalled() 118 + expect(Cooper.greet).toHaveBeenCalled() 119 + 120 + const Max = new Dog('Max') 121 + 122 + // methods are not shared between instances if you assigned them directly 123 + expect(Max.speak).not.toHaveBeenCalled() 124 + expect(Max.greet).not.toHaveBeenCalled() 125 + ``` 126 + 127 + We can reassign the return value for a specific instance: 128 + 129 + ```ts 130 + const dog = new Dog('Cooper') 131 + 132 + // "vi.mocked" is a type helper, since 133 + // TypeScript doesn't know that Dog is a mocked class, 134 + // it wraps any function in a Mock<T> type 135 + // without validating if the function is a mock 136 + vi.mocked(dog.speak).mockReturnValue('woof woof') 137 + 138 + dog.speak() // woof woof 139 + ``` 140 + 141 + To mock the property, we can use the `vi.spyOn(dog, 'name', 'get')` method. This makes it possible to use spy assertions on the mocked property: 142 + 143 + ```ts 144 + const dog = new Dog('Cooper') 145 + 146 + const nameSpy = vi.spyOn(dog, 'name', 'get').mockReturnValue('Max') 147 + 148 + expect(dog.name).toBe('Max') 149 + expect(nameSpy).toHaveBeenCalledTimes(1) 150 + ``` 151 + 152 + ::: tip 153 + You can also spy on getters and setters using the same method. 154 + ::: 155 + 156 + ::: danger 157 + Using classes with `vi.fn()` was introduced in Vitest 4. Previously, you had to use `function` and `prototype` inheritence directly. See [v3 guide](https://v3.vitest.dev/guide/mocking.html#classes). 158 + :::
+52
docs/guide/mocking/dates.md
··· 1 + # Mocking Dates 2 + 3 + Sometimes you need to be in control of the date to ensure consistency when testing. Vitest uses [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers) package for manipulating timers, as well as system date. You can find more about the specific API in detail [here](/api/vi#vi-setsystemtime). 4 + 5 + ## Example 6 + 7 + ```js 8 + import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' 9 + 10 + const businessHours = [9, 17] 11 + 12 + function purchase() { 13 + const currentHour = new Date().getHours() 14 + const [open, close] = businessHours 15 + 16 + if (currentHour > open && currentHour < close) { 17 + return { message: 'Success' } 18 + } 19 + 20 + return { message: 'Error' } 21 + } 22 + 23 + describe('purchasing flow', () => { 24 + beforeEach(() => { 25 + // tell vitest we use mocked time 26 + vi.useFakeTimers() 27 + }) 28 + 29 + afterEach(() => { 30 + // restoring date after each test run 31 + vi.useRealTimers() 32 + }) 33 + 34 + it('allows purchases within business hours', () => { 35 + // set hour within business hours 36 + const date = new Date(2000, 1, 1, 13) 37 + vi.setSystemTime(date) 38 + 39 + // access Date.now() will result in the date set above 40 + expect(purchase()).toEqual({ message: 'Success' }) 41 + }) 42 + 43 + it('disallows purchases outside of business hours', () => { 44 + // set hour outside business hours 45 + const date = new Date(2000, 1, 1, 19) 46 + vi.setSystemTime(date) 47 + 48 + // access Date.now() will result in the date set above 49 + expect(purchase()).toEqual({ message: 'Error' }) 50 + }) 51 + }) 52 + ```
+74
docs/guide/mocking/file-system.md
··· 1 + # Mocking the File System 2 + 3 + Mocking the file system ensures that the tests do not depend on the actual file system, making the tests more reliable and predictable. This isolation helps in avoiding side effects from previous tests. It allows for testing error conditions and edge cases that might be difficult or impossible to replicate with an actual file system, such as permission issues, disk full scenarios, or read/write errors. 4 + 5 + Vitest doesn't provide any file system mocking API out of the box. You can use `vi.mock` to mock the `fs` module manually, but it's hard to maintain. Instead, we recommend using [`memfs`](https://www.npmjs.com/package/memfs) to do that for you. `memfs` creates an in-memory file system, which simulates file system operations without touching the actual disk. This approach is fast and safe, avoiding any potential side effects on the real file system. 6 + 7 + ## Example 8 + 9 + To automatically redirect every `fs` call to `memfs`, you can create `__mocks__/fs.cjs` and `__mocks__/fs/promises.cjs` files at the root of your project: 10 + 11 + ::: code-group 12 + ```ts [__mocks__/fs.cjs] 13 + // we can also use `import`, but then 14 + // every export should be explicitly defined 15 + 16 + const { fs } = require('memfs') 17 + module.exports = fs 18 + ``` 19 + 20 + ```ts [__mocks__/fs/promises.cjs] 21 + // we can also use `import`, but then 22 + // every export should be explicitly defined 23 + 24 + const { fs } = require('memfs') 25 + module.exports = fs.promises 26 + ``` 27 + ::: 28 + 29 + ```ts [read-hello-world.js] 30 + import { readFileSync } from 'node:fs' 31 + 32 + export function readHelloWorld(path) { 33 + return readFileSync(path, 'utf-8') 34 + } 35 + ``` 36 + 37 + ```ts [hello-world.test.js] 38 + import { beforeEach, expect, it, vi } from 'vitest' 39 + import { fs, vol } from 'memfs' 40 + import { readHelloWorld } from './read-hello-world.js' 41 + 42 + // tell vitest to use fs mock from __mocks__ folder 43 + // this can be done in a setup file if fs should always be mocked 44 + vi.mock('node:fs') 45 + vi.mock('node:fs/promises') 46 + 47 + beforeEach(() => { 48 + // reset the state of in-memory fs 49 + vol.reset() 50 + }) 51 + 52 + it('should return correct text', () => { 53 + const path = '/hello-world.txt' 54 + fs.writeFileSync(path, 'hello world') 55 + 56 + const text = readHelloWorld(path) 57 + expect(text).toBe('hello world') 58 + }) 59 + 60 + it('can return a value multiple times', () => { 61 + // you can use vol.fromJSON to define several files 62 + vol.fromJSON( 63 + { 64 + './dir1/hw.txt': 'hello dir1', 65 + './dir2/hw.txt': 'hello dir2', 66 + }, 67 + // default cwd 68 + '/tmp', 69 + ) 70 + 71 + expect(readHelloWorld('/tmp/dir1/hw.txt')).toBe('hello dir1') 72 + expect(readHelloWorld('/tmp/dir2/hw.txt')).toBe('hello dir2') 73 + }) 74 + ```
+61
docs/guide/mocking/functions.md
··· 1 + # Mocking Functions 2 + 3 + Mocking functions can be split up into two different categories: spying and mocking. 4 + 5 + If you need to observe the behaviour of a method on an object, you can use [`vi.spyOn()`](/api/vi#vi-spyon) to create a spy that tracks calls to that method. 6 + 7 + If you need to pass down a custom function implementation as an argument or create a new mocked entity, you can use [`vi.fn()`](/api/vi#vi-fn) to create a mock function. 8 + 9 + Both `vi.spyOn` and `vi.fn` share the same methods. 10 + 11 + ## Example 12 + 13 + ```js 14 + import { afterEach, describe, expect, it, vi } from 'vitest' 15 + 16 + const messages = { 17 + items: [ 18 + { message: 'Simple test message', from: 'Testman' }, 19 + // ... 20 + ], 21 + addItem(item) { 22 + messages.items.push(item) 23 + messages.callbacks.forEach(callback => callback(item)) 24 + }, 25 + onItem(callback) { 26 + messages.callbacks.push(callback) 27 + }, 28 + getLatest, // can also be a `getter or setter if supported` 29 + } 30 + 31 + function getLatest(index = messages.items.length - 1) { 32 + return messages.items[index] 33 + } 34 + 35 + it('should get the latest message with a spy', () => { 36 + const spy = vi.spyOn(messages, 'getLatest') 37 + expect(spy.getMockName()).toEqual('getLatest') 38 + 39 + expect(messages.getLatest()).toEqual( 40 + messages.items[messages.items.length - 1], 41 + ) 42 + 43 + expect(spy).toHaveBeenCalledTimes(1) 44 + 45 + spy.mockImplementationOnce(() => 'access-restricted') 46 + expect(messages.getLatest()).toEqual('access-restricted') 47 + 48 + expect(spy).toHaveBeenCalledTimes(2) 49 + }) 50 + 51 + it('passing down the mock', () => { 52 + const callback = vi.fn() 53 + messages.onItem(callback) 54 + 55 + messages.addItem({ message: 'Another test message', from: 'Testman' }) 56 + expect(callback).toHaveBeenCalledWith({ 57 + message: 'Another test message', 58 + from: 'Testman', 59 + }) 60 + }) 61 + ```
+20
docs/guide/mocking/globals.md
··· 1 + # Mocking Globals 2 + 3 + You can mock global variables that are not present with `jsdom` or `node` by using [`vi.stubGlobal`](/api/vi#vi-stubglobal) helper. It will put the value of the global variable into a `globalThis` object. 4 + 5 + By default, Vitest does not reset these globals, but you can turn on the [`unstubGlobals`](/config/#unstubglobals) option in your config to restore the original values after each test or call [`vi.unstubAllGlobals()`](/api/vi#vi-unstuballglobals) manually. 6 + 7 + ```ts 8 + import { vi } from 'vitest' 9 + 10 + const IntersectionObserverMock = vi.fn(() => ({ 11 + disconnect: vi.fn(), 12 + observe: vi.fn(), 13 + takeRecords: vi.fn(), 14 + unobserve: vi.fn(), 15 + })) 16 + 17 + vi.stubGlobal('IntersectionObserver', IntersectionObserverMock) 18 + 19 + // now you can access it as `IntersectionObserver` or `window.IntersectionObserver` 20 + ```
+412
docs/guide/mocking/modules.md
··· 1 + # Mocking Modules 2 + 3 + ## Defining a Module 4 + 5 + Before mocking a "module", we should define what it is. In Vitest context, the "module" is a file that exports something. Using [plugins](https://vite.dev/guide/api-plugin.html), any file can be turned into a JavaScript module. The "module object" is a namespace object that holds dynamic references to exported identifiers. Simply put, it's an object with exported methods and properties. In this example, `example.js` is a module that exports `method` and `variable`: 6 + 7 + ```js [example.js] 8 + export function answer() { 9 + // ... 10 + return 42 11 + } 12 + 13 + export const variable = 'example' 14 + ``` 15 + 16 + The `exampleObject` here is a module object: 17 + 18 + ```js [example.test.js] 19 + import * as exampleObject from './example.js' 20 + ``` 21 + 22 + The `exampleObject` will always exist even if you imported the example using named imports: 23 + 24 + ```js [example.test.js] 25 + import { answer, variable } from './example.js' 26 + ``` 27 + 28 + You can only reference `exampleObject` outside the example module itself. For example, in a test. 29 + 30 + ## Mocking a Module 31 + 32 + For the purpose of this guide, let's introduce some definitions. 33 + 34 + - **Mocked module** is a module that was completely replaced with another one. 35 + - **Spied module** is a mocked module, but its exported methods keep the original implementation. They can also be tracked. 36 + - **Mocked export** is a module export, which invocations can be tracked. 37 + - **Spied export** is a mocked export. 38 + 39 + To mock a module completely, you can use the [`vi.mock` API](/api/vi#vi-mock). You can define a new module dynamically by providing a factory that returns a new module as a second argument: 40 + 41 + ```ts 42 + import { vi } from 'vitest' 43 + 44 + // The ./example.js module will be replaced with 45 + // the result of a factory function, and the 46 + // original ./example.js module will never be called 47 + vi.mock(import('./example.js'), () => { 48 + return { 49 + answer() { 50 + // ... 51 + return 42 52 + }, 53 + variable: 'mock', 54 + } 55 + }) 56 + ``` 57 + 58 + ::: tip 59 + Remember that you can call `vi.mock` in a [setup file](/config/#setupfiles) to apply the module mock in every test file automatically. 60 + ::: 61 + 62 + ::: tip 63 + Note the usage of dynamic import: `import('./example.ts')`. Vitest will strip it before the code is executed, but it allows TypeScript to properly validate the string and type the `importOriginal` method in your IDE or CLI. 64 + ::: 65 + 66 + If your code is trying to access a method that was not returned from this factory, Vitest will throw an error with a helpful message. Note that `answer` is not mocked, i.e. it cannot be tracked. To make it trackable, use `vi.fn()` instead: 67 + 68 + ```ts 69 + import { vi } from 'vitest' 70 + 71 + vi.mock(import('./example.js'), () => { 72 + return { 73 + answer: vi.fn(), 74 + variable: 'mock', 75 + } 76 + }) 77 + ``` 78 + 79 + The factory method accepts an `importOriginal` function that will execute the original module and return its module object: 80 + 81 + ```ts 82 + import { expect, vi } from 'vitest' 83 + import { answer } from './example.js' 84 + 85 + vi.mock(import('./example.js'), async (importOriginal) => { 86 + const originalModule = await importOriginal() 87 + return { 88 + answer: vi.fn(originalModule.answer), 89 + variable: 'mock', 90 + } 91 + }) 92 + 93 + expect(answer()).toBe(42) 94 + 95 + expect(answer).toHaveBeenCalled() 96 + expect(answer).toHaveReturned(42) 97 + ``` 98 + 99 + ::: warning 100 + Note that `importOriginal` is asynchronous and needs to be awaited. 101 + ::: 102 + 103 + In the above example, we provided the original `answer` to the `vi.fn()` call so it can keep calling it while being tracked at the same time. 104 + 105 + If you require the use of `importOriginal`, consider spying on the export directly via another API: `vi.spyOn`. Instead of replacing the whole module, you can spy only on a single exported method. To do that, you need to import the module as a namespace object: 106 + 107 + ```ts 108 + import { expect, vi } from 'vitest' 109 + import * as exampleObject from './example.js' 110 + 111 + const spy = vi.spyOn(exampleObject, 'answer').mockReturnValue(0) 112 + 113 + expect(exampleObject.answer()).toBe(0) 114 + expect(exampleObject.answer).toHaveBeenCalled() 115 + ``` 116 + 117 + ::: danger Browser Mode Support 118 + This will not work in the [Browser Mode](/guide/browser/) because it uses the browser's native ESM support to serve modules. The module namespace object is sealed and can't be reconfigured. To bypass this limitation, Vitest supports `{ spy: true }` option in `vi.mock('./example.js')`. This will automatically spy on every export in the module without replacing them with fake ones. 119 + 120 + ```ts 121 + import { vi } from 'vitest' 122 + import * as exampleObject from './example.js' 123 + 124 + vi.mock('./example.js', { spy: true }) 125 + 126 + vi.mocked(exampleObject.answer).mockReturnValue(0) 127 + ``` 128 + ::: 129 + 130 + ::: warning 131 + You only need to import the module as a namespace object in the file where you are using the `vi.spyOn` utility. If the `answer` is called in another file and is imported there as a named export, Vitest will be able to properly track it as long as the function that called it is called after `vi.spyOn`: 132 + 133 + ```ts [source.js] 134 + import { answer } from './example.js' 135 + 136 + export function question() { 137 + if (answer() === 42) { 138 + return 'Ultimate Question of Life, the Universe, and Everything' 139 + } 140 + 141 + return 'Unknown Question' 142 + } 143 + ``` 144 + ::: 145 + 146 + Note that `vi.spyOn` will only spy on calls that were done after it spied on the method. So, if the function is executed at the top level during an import or it was called before the spying, `vi.spyOn` will not be able to report on it. 147 + 148 + To automatically mock any module before it is imported, you can call `vi.mock` with a path: 149 + 150 + ```ts 151 + import { vi } from 'vitest' 152 + 153 + vi.mock(import('./example.js')) 154 + ``` 155 + 156 + If the file `./__mocks__/example.js` exists, then Vitest will load it instead. Otherwise, Vitest will load the original module and replace everything recursively: 157 + 158 + {#automocking-algorithm} 159 + 160 + - All arrays will be empty 161 + - All primitives will stay untouched 162 + - All getters will return `undefined` 163 + - All methods will return `undefined` 164 + - All objects will be deeply cloned 165 + - All instances of classes and their prototypes will be cloned 166 + 167 + To disable this behavior, you can pass down `spy: true` as the second argument: 168 + 169 + ```ts 170 + import { vi } from 'vitest' 171 + 172 + vi.mock(import('./example.js'), { spy: true }) 173 + ``` 174 + 175 + Instead of returning `undefined`, all methods will call the original implementation, but you can still keep track of these calls: 176 + 177 + ```ts 178 + import { expect, vi } from 'vitest' 179 + import { answer } from './example.js' 180 + 181 + vi.mock(import('./example.js'), { spy: true }) 182 + 183 + // calls the original implementation 184 + expect(answer()).toBe(42) 185 + // vitest can still track the invocations 186 + expect(answer).toHaveBeenCalled() 187 + ``` 188 + 189 + One nice thing that mocked modules support is sharing the state between the instance and its prototype. Consider this module: 190 + 191 + ```ts [answer.js] 192 + export class Answer { 193 + constructor(value) { 194 + this._value = value 195 + } 196 + 197 + value() { 198 + return this._value 199 + } 200 + } 201 + ``` 202 + 203 + By mocking it, we can keep track of every invocation of `.value()` even without having access to the instance itself: 204 + 205 + ```ts [answer.test.js] 206 + import { expect, test, vi } from 'vitest' 207 + import { Answer } from './answer.js' 208 + 209 + vi.mock(import('./answer.js'), { spy: true }) 210 + 211 + test('instance inherits the state', () => { 212 + // these invocations could be private inside another function 213 + // that you don't have access to in your test 214 + const answer1 = new Answer(42) 215 + const answer2 = new Answer(0) 216 + 217 + expect(answer1.value()).toBe(42) 218 + expect(answer1.value).toHaveBeenCalled() 219 + // note that different instances have their own states 220 + expect(answer2.value).not.toHaveBeenCalled() 221 + 222 + expect(answer2.value()).toBe(0) 223 + 224 + // but the prototype state accumulates all calls 225 + expect(Answer.prototype.value).toHaveBeenCalledTimes(2) 226 + expect(Answer.prototype.value).toHaveReturned(42) 227 + expect(Answer.prototype.value).toHaveReturned(0) 228 + }) 229 + ``` 230 + 231 + This can be very useful to track calls to instances that are never exposed. 232 + 233 + ## Mocking Non-existing Module 234 + 235 + Vitest supports mocking virtual modules. These modules don't exist on the file system, but your code imports them. For example, this can happen when your development environment is different from production. One common example is mocking `vscode` APIs in your unit tests. 236 + 237 + By default, Vitest will fail transforming files if it cannot find the source of the import. To bypass this, you need to specify it in your config. You can either always redirect the import to a file, or just signal Vite to ignore it and use the `vi.mock` factory to define its exports. 238 + 239 + To redirect the import, use [`test.alias`](/config/#alias) config option: 240 + 241 + ```ts [vitest.config.ts] 242 + import { defineConfig } from 'vitest/config' 243 + import { resolve } from 'node:path' 244 + 245 + export default defineConfig({ 246 + test: { 247 + alias: { 248 + vscode: resolve(import.meta.dirname, './mock/vscode.js'), 249 + }, 250 + }, 251 + }) 252 + ``` 253 + 254 + To mark the module as always resolved, return the same string from `resolveId` hook of a plugin: 255 + 256 + ```ts [vitest.config.ts] 257 + import { defineConfig } from 'vitest/config' 258 + import { resolve } from 'node:path' 259 + 260 + export default defineConfig({ 261 + plugins: [ 262 + { 263 + name: 'virtual-vscode', 264 + resolveId(id) { 265 + if (id === 'vscode') { 266 + return 'vscode' 267 + } 268 + } 269 + } 270 + ] 271 + }) 272 + ``` 273 + 274 + Now you can use `vi.mock` as usual in your tests: 275 + 276 + ```ts 277 + import { vi } from 'vitest' 278 + 279 + vi.mock(import('vscode'), () => { 280 + return { 281 + window: { 282 + createOutputChannel: vi.fn(), 283 + } 284 + } 285 + }) 286 + ``` 287 + 288 + ## How it Works 289 + 290 + Vitest implements different module mocking mechanisms depending on the environment. The only feature they share is the plugin transformer. When Vitest sees that a file has `vi.mock` inside, it will transform every static import into a dynamic one and move the `vi.mock` call to the top of the file. This allows Vitest to register the mock before the import happens without breaking the ESM rule of hoisted imports. 291 + 292 + ::: code-group 293 + ```ts [example.js] 294 + import { answer } from './answer.js' 295 + 296 + vi.mock(import('./answer.js')) 297 + 298 + console.log(answer) 299 + ``` 300 + ```ts [example.transformed.js] 301 + vi.mock('./answer.js') 302 + 303 + const __vitest_module_0__ = await __handle_mock__( 304 + () => import('./answer.js') 305 + ) 306 + // to keep the live binding, we have to access 307 + // the export on the module namespace 308 + console.log(__vitest_module_0__.answer()) 309 + ``` 310 + ::: 311 + 312 + The `__handle_mock__` wrapper just makes sure the mock is resolved before the import is initiated, it doesn't modify the module in any way. 313 + 314 + The module mocking plugins are available in the [`@vitest/mocker` package](https://github.com/vitest-dev/vitest/tree/main/packages/mocker). 315 + 316 + ### JSDOM, happy-dom, Node 317 + 318 + When you run your tests in an emulated environment, Vitest creates a [module runner](https://vite.dev/guide/api-environment-runtimes.html#modulerunner) that can consume Vite code. The module runner is designed in such a way that Vitest can hook into the module evaluation and replace it with the mock, if it was registered. This means that Vitest runs your code in an ESM-like environment, but it doesn't use native ESM mechanism directly. This allows the test runner to bend the rules around ES Modules immutability, allowing users to call `vi.spyOn` on a seemingly ES Module. 319 + 320 + ### Browser Mode 321 + 322 + Vitest uses native ESM in the Browser Mode. This means that we cannot replace the module so easily. Instead, Vitest intercepts the fetch request (via playwright's `page.route` or a Vite plugin API if using `preview` or `webdriverio`) and serves transformed code, if the module was mocked. 323 + 324 + For example, if the module is automocked, Vitest can parse static exports and create a placeholder module: 325 + 326 + ::: code-group 327 + ```ts [answer.js] 328 + export function answer() { 329 + return 42 330 + } 331 + ``` 332 + ```ts [answer.transformed.js] 333 + function answer() { 334 + return 42 335 + } 336 + 337 + const __private_module__ = { 338 + [Symbol.toStringTag]: 'Module', 339 + answer: vi.fn(answer), 340 + } 341 + 342 + export const answer = __private_module__.answer 343 + ``` 344 + ::: 345 + 346 + The example is simplified for brevity, but the concept is unchanged. We can inject a `__private_module__` variable into the module to hold the mocked values. If the user called `vi.mock` with `spy: true`, we pass down the original value; otherwise, we create a simple `vi.fn()` mock. 347 + 348 + If user defined a custom factory, this makes it harder to inject the code, but not impossible. When the mocked file is served, we first resolve the factory in the browser, then pass down the keys back to the server, and use them to create a placeholder module: 349 + 350 + ```ts 351 + const resolvedFactoryKeys = await resolveBrowserFactory(url) 352 + const mockedModule = ` 353 + const __private_module__ = getFactoryReturnValue(${url}) 354 + ${resolvedFactoryKeys.map(key => `export const ${key} = __private_module__["${key}"]`).join('\n')} 355 + ` 356 + ``` 357 + 358 + This module can now be served back to the browser. You can inspect the code in the devtools when you run the tests. 359 + 360 + ## Mocking Modules Pitfalls 361 + 362 + Beware that it is not possible to mock calls to methods that are called inside other methods of the same file. For example, in this code: 363 + 364 + ```ts [foobar.js] 365 + export function foo() { 366 + return 'foo' 367 + } 368 + 369 + export function foobar() { 370 + return `${foo()}bar` 371 + } 372 + ``` 373 + 374 + It is not possible to mock the `foo` method from the outside because it is referenced directly. So this code will have no effect on the `foo` call inside `foobar` (but it will affect the `foo` call in other modules): 375 + 376 + ```ts [foobar.test.ts] 377 + import { vi } from 'vitest' 378 + import * as mod from './foobar.js' 379 + 380 + // this will only affect "foo" outside of the original module 381 + vi.spyOn(mod, 'foo') 382 + vi.mock(import('./foobar.js'), async (importOriginal) => { 383 + return { 384 + ...await importOriginal(), 385 + // this will only affect "foo" outside of the original module 386 + foo: () => 'mocked' 387 + } 388 + }) 389 + ``` 390 + 391 + You can confirm this behavior by providing the implementation to the `foobar` method directly: 392 + 393 + ```ts [foobar.test.js] 394 + import * as mod from './foobar.js' 395 + 396 + vi.spyOn(mod, 'foo') 397 + 398 + // exported foo references mocked method 399 + mod.foobar(mod.foo) 400 + ``` 401 + 402 + ```ts [foobar.js] 403 + export function foo() { 404 + return 'foo' 405 + } 406 + 407 + export function foobar(injectedFoo) { 408 + return injectedFoo === foo // false 409 + } 410 + ``` 411 + 412 + This is the intended behavior, and we do not plan to implement a workaround. Consider refactoring your code into multiple files or use techniques such as [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection). We believe that making the application testable is not the responsibility of the test runner, but of the application architecture.
+114
docs/guide/mocking/requests.md
··· 1 + # Mocking Requests 2 + 3 + Because Vitest runs in Node, mocking network requests is tricky; web APIs are not available, so we need something that will mimic network behavior for us. We recommend [Mock Service Worker](https://mswjs.io/) to accomplish this. It allows you to mock `http`, `WebSocket` and `GraphQL` network requests, and is framework agnostic. 4 + 5 + Mock Service Worker (MSW) works by intercepting the requests your tests make, allowing you to use it without changing any of your application code. In-browser, this uses the [Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API). In Node.js, and for Vitest, it uses the [`@mswjs/interceptors`](https://github.com/mswjs/interceptors) library. To learn more about MSW, read their [introduction](https://mswjs.io/docs/) 6 + 7 + ## Configuration 8 + 9 + You can use it like below in your [setup file](/config/#setupfiles) 10 + 11 + ::: code-group 12 + 13 + ```js [HTTP Setup] 14 + import { afterAll, afterEach, beforeAll } from 'vitest' 15 + import { setupServer } from 'msw/node' 16 + import { http, HttpResponse } from 'msw' 17 + 18 + const posts = [ 19 + { 20 + userId: 1, 21 + id: 1, 22 + title: 'first post title', 23 + body: 'first post body', 24 + }, 25 + // ... 26 + ] 27 + 28 + export const restHandlers = [ 29 + http.get('https://rest-endpoint.example/path/to/posts', () => { 30 + return HttpResponse.json(posts) 31 + }), 32 + ] 33 + 34 + const server = setupServer(...restHandlers) 35 + 36 + // Start server before all tests 37 + beforeAll(() => server.listen({ onUnhandledRequest: 'error' })) 38 + 39 + // Close server after all tests 40 + afterAll(() => server.close()) 41 + 42 + // Reset handlers after each test for test isolation 43 + afterEach(() => server.resetHandlers()) 44 + ``` 45 + 46 + ```js [GraphQL Setup] 47 + import { afterAll, afterEach, beforeAll } from 'vitest' 48 + import { setupServer } from 'msw/node' 49 + import { graphql, HttpResponse } from 'msw' 50 + 51 + const posts = [ 52 + { 53 + userId: 1, 54 + id: 1, 55 + title: 'first post title', 56 + body: 'first post body', 57 + }, 58 + // ... 59 + ] 60 + 61 + const graphqlHandlers = [ 62 + graphql.query('ListPosts', () => { 63 + return HttpResponse.json({ 64 + data: { posts }, 65 + }) 66 + }), 67 + ] 68 + 69 + const server = setupServer(...graphqlHandlers) 70 + 71 + // Start server before all tests 72 + beforeAll(() => server.listen({ onUnhandledRequest: 'error' })) 73 + 74 + // Close server after all tests 75 + afterAll(() => server.close()) 76 + 77 + // Reset handlers after each test for test isolation 78 + afterEach(() => server.resetHandlers()) 79 + ``` 80 + 81 + ```js [WebSocket Setup] 82 + import { afterAll, afterEach, beforeAll } from 'vitest' 83 + import { setupServer } from 'msw/node' 84 + import { ws } from 'msw' 85 + 86 + const chat = ws.link('wss://chat.example.com') 87 + 88 + const wsHandlers = [ 89 + chat.addEventListener('connection', ({ client }) => { 90 + client.addEventListener('message', (event) => { 91 + console.log('Received message from client:', event.data) 92 + // Echo the received message back to the client 93 + client.send(`Server received: ${event.data}`) 94 + }) 95 + }), 96 + ] 97 + 98 + const server = setupServer(...wsHandlers) 99 + 100 + // Start server before all tests 101 + beforeAll(() => server.listen({ onUnhandledRequest: 'error' })) 102 + 103 + // Close server after all tests 104 + afterAll(() => server.close()) 105 + 106 + // Reset handlers after each test for test isolation 107 + afterEach(() => server.resetHandlers()) 108 + ``` 109 + ::: 110 + 111 + > Configuring the server with `onUnhandledRequest: 'error'` ensures that an error is thrown whenever there is a request that does not have a corresponding request handler. 112 + 113 + ## More 114 + There is much more to MSW. You can access cookies and query parameters, define mock error responses, and much more! To see all you can do with MSW, read [their documentation](https://mswjs.io/docs).
+48
docs/guide/mocking/timers.md
··· 1 + # Timers 2 + 3 + When we test code that involves timeouts or intervals, instead of having our tests wait it out or timeout, we can speed up our tests by using "fake" timers that mock calls to `setTimeout` and `setInterval`. 4 + 5 + See the [`vi.useFakeTimers` API section](/api/vi#vi-usefaketimers) for a more in depth detailed API description. 6 + 7 + ## Example 8 + 9 + ```js 10 + import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' 11 + 12 + function executeAfterTwoHours(func) { 13 + setTimeout(func, 1000 * 60 * 60 * 2) // 2 hours 14 + } 15 + 16 + function executeEveryMinute(func) { 17 + setInterval(func, 1000 * 60) // 1 minute 18 + } 19 + 20 + const mock = vi.fn(() => console.log('executed')) 21 + 22 + describe('delayed execution', () => { 23 + beforeEach(() => { 24 + vi.useFakeTimers() 25 + }) 26 + afterEach(() => { 27 + vi.restoreAllMocks() 28 + }) 29 + it('should execute the function', () => { 30 + executeAfterTwoHours(mock) 31 + vi.runAllTimers() 32 + expect(mock).toHaveBeenCalledTimes(1) 33 + }) 34 + it('should not execute the function', () => { 35 + executeAfterTwoHours(mock) 36 + // advancing by 2ms won't trigger the func 37 + vi.advanceTimersByTime(2) 38 + expect(mock).not.toHaveBeenCalled() 39 + }) 40 + it('should execute every minute', () => { 41 + executeEveryMinute(mock) 42 + vi.advanceTimersToNextTimer() 43 + expect(mock).toHaveBeenCalledTimes(1) 44 + vi.advanceTimersToNextTimer() 45 + expect(mock).toHaveBeenCalledTimes(2) 46 + }) 47 + }) 48 + ```
+3 -3
packages/vitest/src/integrations/vi.ts
··· 192 192 * The call to `vi.mock` is hoisted to the top of the file, so you don't have access to variables declared in the global file scope 193 193 * unless they are defined with [`vi.hoisted`](https://vitest.dev/api/vi#vi-hoisted) before this call. 194 194 * 195 - * Mocking algorithm is described in [documentation](https://vitest.dev/guide/mocking#modules). 195 + * Mocking algorithm is described in [documentation](https://vitest.dev/guide/mocking/modules). 196 196 * @param path Path to the module. Can be aliased, if your Vitest config supports it 197 197 * @param factory Mocked module factory. The result of this function will be an exports object 198 198 */ ··· 217 217 * 218 218 * Unlike [`vi.mock`](https://vitest.dev/api/vi#vi-mock), this method will not mock statically imported modules because it is not hoisted to the top of the file. 219 219 * 220 - * Mocking algorithm is described in [documentation](https://vitest.dev/guide/mocking#modules). 220 + * Mocking algorithm is described in [documentation](https://vitest.dev/guide/mocking/modules). 221 221 * @param path Path to the module. Can be aliased, if your Vitest config supports it 222 222 * @param factory Mocked module factory. The result of this function will be an exports object 223 223 */ ··· 254 254 /** 255 255 * Imports a module with all of its properties and nested properties mocked. 256 256 * 257 - * Mocking algorithm is described in [documentation](https://vitest.dev/guide/mocking#modules). 257 + * Mocking algorithm is described in [documentation](https://vitest.dev/guide/mocking/modules). 258 258 * @example 259 259 * ```ts 260 260 * const example = await vi.importMock<typeof import('./example.js')>('./example.js')