1 | [![Build Status](https://github.com/tschaub/mock-fs/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/tschaub/mock-fs/actions?workflow=Test)
|
2 |
|
3 | # `mock-fs`
|
4 |
|
5 | The `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 |
|
9 | The 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
|
12 | const mock = require('mock-fs');
|
13 |
|
14 | mock({
|
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 |
|
24 | When 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
|
28 | mock.restore();
|
29 | ```
|
30 |
|
31 | ## Upgrading to version 4
|
32 |
|
33 | Instead 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 |
|
35 | Breaking 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 |
|
42 | Some of these breaking changes may be restored in a future release.
|
43 |
|
44 | ## Docs
|
45 |
|
46 | ### <a id='mockconfigoptions'>`mock(config, options)`</a>
|
47 |
|
48 | Configure the `fs` module so it is backed by an in-memory file system.
|
49 |
|
50 | Calling `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 |
|
52 | Property 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 |
|
58 | The 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 |
|
65 | You 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
|
82 | mock({
|
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 |
|
101 | When `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
|
103 | mock({
|
104 | 'path/to/file.txt': 'file content here'
|
105 | });
|
106 | ```
|
107 |
|
108 | To 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 |
|
112 | Create 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 |
|
123 | To create a mock filesystem with a very old file named `foo`, you could do something like this:
|
124 | ```js
|
125 | mock({
|
126 | foo: mock.file({
|
127 | content: 'file content here',
|
128 | ctime: new Date(1),
|
129 | mtime: new Date(1)
|
130 | })
|
131 | });
|
132 | ```
|
133 |
|
134 | Note 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 |
|
138 | When `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 */ }})
|
142 | mock({
|
143 | path: {
|
144 | to: {
|
145 | dir: {
|
146 | file1: 'text content',
|
147 | file2: Buffer.from([1, 2, 3, 4])
|
148 | }
|
149 | }
|
150 | }
|
151 | });
|
152 | ```
|
153 |
|
154 | To 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 |
|
158 | Create 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 |
|
169 | To 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
|
171 | mock({
|
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 |
|
182 | Note 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 |
|
186 | Using 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 |
|
190 | Create 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 |
|
201 | To create a mock filesystem with a file and a symlink, you could do something like this:
|
202 | ```js
|
203 | mock({
|
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 |
|
217 | Restore 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
|
220 | beforeEach(function() {
|
221 | mock({
|
222 | 'fake-file': 'file contents'
|
223 | });
|
224 | });
|
225 | afterEach(mock.restore);
|
226 | ```
|
227 |
|
228 | ### Bypassing the mock file system
|
229 |
|
230 | #### <a id='mockbypass'>`mock.bypass(fn)`</a>
|
231 |
|
232 | Execute 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
|
236 | const realFilePath = '/path/to/real/file.txt';
|
237 | const myData = mock.bypass(() => fs.readFileSync(realFilePath, 'utf-8'));
|
238 | ```
|
239 |
|
240 | If 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 |
|
244 | Asynchronous calls are supported, however, they are not recommended as they could produce unintended consequences if
|
245 | anything else tries to access the mocked filesystem before they've completed.
|
246 |
|
247 | ```js
|
248 | async 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 |
|
259 | Using `npm`:
|
260 |
|
261 | ```
|
262 | npm install mock-fs --save-dev
|
263 | ```
|
264 |
|
265 | ## Caveats
|
266 |
|
267 | When 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 |
|
271 | Mock 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 |
|
273 | Tested on Linux, OSX, and Windows using Node 16 through 18. 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.
|
278 | If `mockFs` is active, Jest isn't able to load existing snapshots. In such case it accepts all snapshots
|
279 | without diffing the old ones, which breaks the concept of snapshot testing.
|
280 |
|
281 | Calling `mock.restore()` in `afterEach` is too late and it's necessary to call it before snapshot matching:
|
282 |
|
283 | ```js
|
284 | const actual = testedFunction()
|
285 | mock.restore()
|
286 | expect(actual).toMatchSnapshot()
|
287 | ```
|
288 |
|
289 | Note: it's safe to call `mock.restore` multiple times, so it can still be called in `afterEach` and then manually
|
290 | in test cases which use snapshot testing. |
\ | No newline at end of file |