UNPKG

12.4 kBMarkdownView Raw
1[![Build Status](https://github.com/tschaub/mock-fs/workflows/Test/badge.svg)](https://github.com/tschaub/mock-fs/actions?workflow=Test)
2
3# `mock-fs`
4
5The `mock-fs` module allows Node's built-in [`fs` module](http://nodejs.org/api/fs.html) to be backed temporarily by an in-memory, mock file system. This lets you run tests against a set of mock files and directories instead of lugging around a bunch of test fixtures.
6
7## Example
8
9The code below makes it so the `fs` module is temporarily backed by a mock file system with a few files and directories.
10
11```js
12const mock = require('mock-fs');
13
14mock({
15 'path/to/fake/dir': {
16 'some-file.txt': 'file content here',
17 'empty-dir': {/** empty directory */}
18 },
19 'path/to/some.png': Buffer.from([8, 6, 7, 5, 3, 0, 9]),
20 'some/other/path': {/** another empty directory */}
21});
22```
23
24When you are ready to restore the `fs` module (so that it is backed by your real file system), call [`mock.restore()`](#mockrestore). Note that calling this may be **mandatory** in some cases. See [istanbuljs/nyc#324](https://github.com/istanbuljs/nyc/issues/324#issuecomment-234018654)
25
26```js
27// after a test runs
28mock.restore();
29```
30
31## Upgrading to version 4
32
33Instead of overriding all methods of the built-in `fs` module, the library now overrides `process.binding('fs')`. The purpose of this change is to avoid conflicts with other libraries that override `fs` methods (e.g. `graceful-fs`) and to make it possible to work with multiple Node releases without maintaining copied and slightly modified versions of Node's `fs` module.
34
35Breaking changes:
36
37 * The `mock.fs()` function has been removed. This returned an object with `fs`-like methods without overriding the built-in `fs` module.
38 * The object created by `fs.Stats` is no longer an instance of `fs.Stats` (though it has all the same properties and methods).
39 * Lazy `require()` do not use the real filesystem.
40 * Tests are no longer run in Node < 4.
41
42Some of these breaking changes may be restored in a future release.
43
44## Docs
45
46### <a id='mockconfigoptions'>`mock(config, options)`</a>
47
48Configure the `fs` module so it is backed by an in-memory file system.
49
50Calling `mock` sets up a mock file system with two directories by default: `process.cwd()` and `os.tmpdir()` (or `os.tmpDir()` for older Node). When called with no arguments, just these two directories are created. When called with a `config` object, additional files, directories, and symlinks are created. To avoid creating a directory for `process.cwd()` and `os.tmpdir()`, see the [`options`](#options) below.
51
52Property names of the `config` object are interpreted as relative paths to resources (relative from `process.cwd()`). Property values of the `config` object are interpreted as content or configuration for the generated resources.
53
54*Note that paths should always use forward slashes (`/`) - even on Windows.*
55
56### <a id='options'>`options`</a>
57
58The second (optional) argument may include the properties below.
59
60 * `createCwd` - `boolean` Create a directory for `process.cwd()`. This is `true` by default.
61 * `createTmp` - `boolean` Create a directory for `os.tmpdir()`. This is `true` by default.
62
63### Loading real files & directories
64
65You can load real files and directories into the mock system using `mock.load()`
66
67#### Notes
68
69- All stat information is duplicated (dates, permissions, etc)
70- By default, all files are lazy-loaded, unless you specify the `{lazy: false}` option
71
72#### <a id='mappingoptions'>options</a>
73
74| Option | Type | Default | Description |
75| --------- | ------- | ------- | ------------
76| lazy | boolean | true | File content isn't loaded until explicitly read
77| recursive | boolean | true | Load all files and directories recursively
78
79#### `mock.load(path, options)`
80
81```js
82mock({
83 // Lazy-load file
84 'my-file.txt': mock.load(path.resolve(__dirname, 'assets/special-file.txt')),
85
86 // Pre-load js file
87 'ready.js': mock.load(path.resolve(__dirname, 'scripts/ready.js'), {lazy: false}),
88
89 // Recursively loads all node_modules
90 'node_modules': mock.load(path.resolve(__dirname, '../node_modules')),
91
92 // Creates a directory named /tmp with only the files in /tmp/special_tmp_files (no subdirectories), pre-loading all content
93 '/tmp': mock.load('/tmp/special_tmp_files', {recursive: false, lazy:false}),
94
95 'fakefile.txt': 'content here'
96});
97```
98
99### Creating files
100
101When `config` property values are a `string` or `Buffer`, a file is created with the provided content. For example, the following configuration creates a single file with string content (in addition to the two default directories).
102```js
103mock({
104 'path/to/file.txt': 'file content here'
105});
106```
107
108To create a file with additional properties (owner, permissions, atime, etc.), use the [`mock.file()`](#mockfileproperties) function described below.
109
110### <a id='mockfileproperties'>`mock.file(properties)`</a>
111
112Create a factory for new files. Supported properties:
113
114 * **content** - `string|Buffer` File contents.
115 * **mode** - `number` File mode (permission and sticky bits). Defaults to `0666`.
116 * **uid** - `number` The user id. Defaults to `process.getuid()`.
117 * **gid** - `number` The group id. Defaults to `process.getgid()`.
118 * **atime** - `Date` The last file access time. Defaults to `new Date()`. Updated when file contents are accessed.
119 * **ctime** - `Date` The last file change time. Defaults to `new Date()`. Updated when file owner or permissions change.
120 * **mtime** - `Date` The last file modification time. Defaults to `new Date()`. Updated when file contents change.
121 * **birthtime** - `Date` The time of file creation. Defaults to `new Date()`.
122
123To create a mock filesystem with a very old file named `foo`, you could do something like this:
124```js
125mock({
126 foo: mock.file({
127 content: 'file content here',
128 ctime: new Date(1),
129 mtime: new Date(1)
130 })
131});
132```
133
134Note that if you want to create a file with the default properties, you can provide a `string` or `Buffer` directly instead of calling `mock.file()`.
135
136### Creating directories
137
138When `config` property values are an `Object`, a directory is created. The structure of the object is the same as the `config` object itself. So an empty directory can be created with a simple object literal (`{}`). The following configuration creates a directory containing two files (in addition to the two default directories):
139```js
140// note that this could also be written as
141// mock({'path/to/dir': { /** config */ }})
142mock({
143 path: {
144 to: {
145 dir: {
146 file1: 'text content',
147 file2: Buffer.from([1, 2, 3, 4])
148 }
149 }
150 }
151});
152```
153
154To create a directory with additional properties (owner, permissions, atime, etc.), use the [`mock.directory()`](mockdirectoryproperties) function described below.
155
156### <a id='mockdirectoryproperties'>`mock.directory(properties)`</a>
157
158Create a factory for new directories. Supported properties:
159
160 * **mode** - `number` Directory mode (permission and sticky bits). Defaults to `0777`.
161 * **uid** - `number` The user id. Defaults to `process.getuid()`.
162 * **gid** - `number` The group id. Defaults to `process.getgid()`.
163 * **atime** - `Date` The last directory access time. Defaults to `new Date()`.
164 * **ctime** - `Date` The last directory change time. Defaults to `new Date()`. Updated when owner or permissions change.
165 * **mtime** - `Date` The last directory modification time. Defaults to `new Date()`. Updated when an item is added, removed, or renamed.
166 * **birthtime** - `Date` The time of directory creation. Defaults to `new Date()`.
167 * **items** - `Object` Directory contents. Members will generate additional files, directories, or symlinks.
168
169To create a mock filesystem with a directory with the relative path `some/dir` that has a mode of `0755` and two child files, you could do something like this:
170```js
171mock({
172 'some/dir': mock.directory({
173 mode: 0755,
174 items: {
175 file1: 'file one content',
176 file2: Buffer.from([8, 6, 7, 5, 3, 0, 9])
177 }
178 })
179});
180```
181
182Note that if you want to create a directory with the default properties, you can provide an `Object` directly instead of calling `mock.directory()`.
183
184### Creating symlinks
185
186Using a `string` or a `Buffer` is a shortcut for creating files with default properties. Using an `Object` is a shortcut for creating a directory with default properties. There is no shortcut for creating symlinks. To create a symlink, you need to call the [`mock.symlink()`](#mocksymlinkproperties) function described below.
187
188### <a id='mocksymlinkproperties'>`mock.symlink(properties)`</a>
189
190Create a factory for new symlinks. Supported properties:
191
192 * **path** - `string` Path to the source (required).
193 * **mode** - `number` Symlink mode (permission and sticky bits). Defaults to `0666`.
194 * **uid** - `number` The user id. Defaults to `process.getuid()`.
195 * **gid** - `number` The group id. Defaults to `process.getgid()`.
196 * **atime** - `Date` The last symlink access time. Defaults to `new Date()`.
197 * **ctime** - `Date` The last symlink change time. Defaults to `new Date()`.
198 * **mtime** - `Date` The last symlink modification time. Defaults to `new Date()`.
199 * **birthtime** - `Date` The time of symlink creation. Defaults to `new Date()`.
200
201To create a mock filesystem with a file and a symlink, you could do something like this:
202```js
203mock({
204 'some/dir': {
205 'regular-file': 'file contents',
206 'a-symlink': mock.symlink({
207 path: 'regular-file'
208 })
209 }
210});
211```
212
213### Restoring the file system
214
215### <a id='mockrestore'>`mock.restore()`</a>
216
217Restore the `fs` binding to the real file system. This undoes the effect of calling `mock()`. Typically, you would set up a mock file system before running a test and restore the original after. Using a test runner with `beforeEach` and `afterEach` hooks, this might look like the following:
218
219```js
220beforeEach(function() {
221 mock({
222 'fake-file': 'file contents'
223 });
224});
225afterEach(mock.restore);
226```
227
228### Bypassing the mock file system
229
230#### <a id='mockbypass'>`mock.bypass(fn)`</a>
231
232Execute calls to the real filesystem with mock.bypass()
233
234```js
235// This file exists only on the real FS, not on the mocked FS
236const realFilePath = '/path/to/real/file.txt';
237const myData = mock.bypass(() => fs.readFileSync(realFilePath, 'utf-8'));
238```
239
240If you pass an asynchronous function or a promise-returning function to `bypass()`, a promise will be returned.
241
242#### <a id='bypassasync'>Async Warning</a>
243
244Asynchronous calls are supported, however, they are not recommended as they could produce unintended consequences if
245anything else tries to access the mocked filesystem before they've completed.
246
247```js
248async function getFileInfo(fileName) {
249 return await mock.bypass(async () => {
250 const stats = await fs.promises.stat(fileName);
251 const data = await fs.promises.readFile(fileName);
252 return {stats, data};
253 });
254}
255```
256
257## Install
258
259Using `npm`:
260
261```
262npm install mock-fs --save-dev
263```
264
265## Caveats
266
267When you require `mock-fs`, Node's own `fs` module is patched to allow the binding to the underlying file system to be swapped out. If you require `mock-fs` *before* any other modules that modify `fs` (e.g. `graceful-fs`), the mock should behave as expected.
268
269**Note** `mock-fs` is not compatible with `graceful-fs@3.x` but works with `graceful-fs@4.x`.
270
271Mock file access is controlled based on file mode where `process.getuid()` and `process.getgid()` are available (POSIX systems). On other systems (e.g. Windows) the file mode has no effect.
272
273Tested on Linux, OSX, and Windows using Node 12 through 16. Check the tickets for a list of [known issues](https://github.com/tschaub/mock-fs/issues).
274
275### Using with Jest Snapshot Testing
276
277`.toMatchSnapshot` in [Jest](https://jestjs.io/docs/en/snapshot-testing) uses `fs` to load existing snapshots.
278If `mockFs` is active, Jest isn't able to load existing snapshots. In such case it accepts all snapshots
279without diffing the old ones, which breaks the concept of snapshot testing.
280
281Calling `mock.restore()` in `afterEach` is too late and it's necessary to call it before snapshot matching:
282
283```js
284const actual = testedFunction()
285mock.restore()
286expect(actual).toMatchSnapshot()
287```
288
289Note: it's safe to call `mock.restore` multiple times, so it can still be called in `afterEach` and then manually
290in test cases which use snapshot testing.
\No newline at end of file