[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: add mock fs section (#6021)

authored by

Vladimir and committed by
GitHub
(Jul 2, 2024, 1:30 PM +0200) a820b156 368c1372

+94 -6
+77
docs/guide/mocking.md
··· 354 354 }) 355 355 ``` 356 356 357 + ## File System 358 + 359 + 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. 360 + 361 + 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. 362 + 363 + ### Example 364 + 365 + To automatially redirect every `fs` call to `memfs`, you can create `__mocks__/fs.cjs` and `__mocks__/fs/promises.cjs` files at the root of your project: 366 + 367 + ::: code-group 368 + ```ts [__mocks__/fs.cjs] 369 + // we can also use `import`, but then 370 + // every export should be explicitly defined 371 + 372 + const { fs } = require('memfs') 373 + module.exports = fs 374 + ``` 375 + 376 + ```ts [__mocks__/fs/promises.cjs] 377 + // we can also use `import`, but then 378 + // every export should be explicitly defined 379 + 380 + const { fs } = require('memfs') 381 + module.exports = fs.promises 382 + ``` 383 + ::: 384 + 385 + ```ts 386 + // hello-world.js 387 + import { readFileSync } from 'node:fs' 388 + 389 + export function readHelloWorld(path) { 390 + return readFileSync('./hello-world.txt') 391 + } 392 + ``` 393 + 394 + ```ts 395 + // hello-world.test.js 396 + import { beforeEach, expect, it, vi } from 'vitest' 397 + import { fs, vol } from 'memfs' 398 + import { readHelloWorld } from './hello-world.js' 399 + 400 + // tell vitest to use fs mock from __mocks__ folder 401 + // this can be done in a setup file if fs should always be mocked 402 + vi.mock('node:fs') 403 + vi.mock('node:fs/promises') 404 + 405 + beforeEach(() => { 406 + // reset the state of in-memory fs 407 + vol.reset() 408 + }) 409 + 410 + it('should return correct text', () => { 411 + const path = './hello-world.txt' 412 + fs.writeFileSync(path, 'hello world') 413 + 414 + const text = readHelloWorld(path) 415 + expect(text).toBe('hello world') 416 + }) 417 + 418 + it('can return a value multiple times', () => { 419 + // you can use vol.fromJSON to define several files 420 + vol.fromJSON( 421 + { 422 + './dir1/hw.txt': 'hello dir1', 423 + './dir2/hw.txt': 'hello dir2', 424 + }, 425 + // default cwd 426 + '/tmp' 427 + ) 428 + 429 + expect(readHelloWorld('/tmp/dir1/hw.txt')).toBe('hello dir1') 430 + expect(readHelloWorld('/tmp/dir2/hw.txt')).toBe('hello dir2') 431 + }) 432 + ``` 433 + 357 434 ## Requests 358 435 359 436 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 will let you mock both `REST` and `GraphQL` network requests, and is framework agnostic.
+4 -1
packages/web-worker/src/utils.ts
··· 1 - import { readFileSync } from 'node:fs' 1 + import { readFileSync as _readFileSync } from 'node:fs' 2 2 import type { WorkerGlobalState } from 'vitest' 3 3 import ponyfillStructuredClone from '@ungap/structured-clone' 4 4 import createDebug from 'debug' 5 5 import type { CloneOption } from './types' 6 + 7 + // keep the reference in case it was mocked 8 + const readFileSync = _readFileSync 6 9 7 10 export const debug = createDebug('vitest:web-worker') 8 11
+3 -1
packages/vitest/src/runtime/execute.ts
··· 1 1 import vm from 'node:vm' 2 2 import { pathToFileURL } from 'node:url' 3 - import { readFileSync } from 'node:fs' 3 + import fs from 'node:fs' 4 4 import type { ModuleCacheMap } from 'vite-node/client' 5 5 import { DEFAULT_REQUEST_STUBS, ViteNodeRunner } from 'vite-node/client' 6 6 import { ··· 17 17 import type { WorkerGlobalState } from '../types' 18 18 import { VitestMocker } from './mocker' 19 19 import type { ExternalModulesExecutor } from './external-executor' 20 + 21 + const { readFileSync } = fs 20 22 21 23 export interface ExecuteOptions extends ViteNodeRunnerOptions { 22 24 mockMap: MockMap
+3 -1
packages/vitest/src/runtime/external-executor.ts
··· 1 1 import vm from 'node:vm' 2 2 import { fileURLToPath, pathToFileURL } from 'node:url' 3 3 import { dirname } from 'node:path' 4 - import { existsSync, statSync } from 'node:fs' 4 + import fs from 'node:fs' 5 5 import { extname, join, normalize } from 'pathe' 6 6 import { getCachedData, isNodeBuiltin, setCacheData } from 'vite-node/utils' 7 7 import type { RuntimeRPC } from '../types/rpc' ··· 13 13 import { ViteExecutor } from './vm/vite-executor' 14 14 15 15 const SyntheticModule: typeof VMSyntheticModule = (vm as any).SyntheticModule 16 + 17 + const { existsSync, statSync } = fs 16 18 17 19 // always defined when we use vm pool 18 20 const nativeResolve = import.meta.resolve!
+3 -1
packages/vitest/src/runtime/mocker.ts
··· 1 - import { existsSync, readdirSync } from 'node:fs' 1 + import fs from 'node:fs' 2 2 import vm from 'node:vm' 3 3 import { basename, dirname, extname, isAbsolute, join, resolve } from 'pathe' 4 4 import { getType, highlight } from '@vitest/utils' ··· 7 7 import { getAllMockableProperties } from '../utils/base' 8 8 import type { MockFactory, PendingSuiteMock } from '../types/mocker' 9 9 import type { VitestExecutor } from './execute' 10 + 11 + const { existsSync, readdirSync } = fs 10 12 11 13 const spyModulePath = resolve(distDir, 'spy.js') 12 14
+4 -2
packages/vitest/src/runtime/vm/file-map.ts
··· 1 - import { promises as fs, readFileSync } from 'node:fs' 1 + import fs from 'node:fs' 2 + 3 + const { promises, readFileSync } = fs 2 4 3 5 export class FileMap { 4 6 private fsCache = new Map<string, string>() ··· 9 11 if (cached != null) { 10 12 return cached 11 13 } 12 - const source = await fs.readFile(path, 'utf-8') 14 + const source = await promises.readFile(path, 'utf-8') 13 15 this.fsCache.set(path, source) 14 16 return source 15 17 }