Mocking the file system

When writing tests, it’s common to run up against code that relies on file system access. People often end up including a bunch of test fixtures along with their code. Tests might create some temporary space, copy fixtures in place, run code, make assertions about what was read or written, and then clean up. The result can be fragile when working across multiple operating systems and leads to long test run times. In cases like this, a mock file system provides an alternative.

The mock-fs module

In Node, the fs module provides file system access. The mock-fs module allows the built-in fs module to be temporarily backed by an in-memory file system during testing. The code below shows how a mock file system can be set up with a few directories and files:

var mock = require('mock-fs');

mock({
  'path/to/dir': {
    'file1': 'file contents here',
    'file2': new Buffer([8, 6, 7, 5, 3, 0, 9])
  }
});

With the mock file system in place, any code that uses the fs module will have access your fake files.

var assert = require('assert');
var fs = require('fs');

fs.readFile('path/to/dir/file1', function(err, data) {
  assert.equal(String(data), 'file contents here');
});

And when you’re done working with mocks, you can restore access to the real file system.

mock.restore();

The mock() function allows you to create directories, regular files, or symlinks with additional configuration options. For example, you can set the file modification time, user or group id, permissions, and more. On POSIX-compliant systems (where process.getuid() and process.getgid() are available), file access is restricted based on the mode setting. See the readme for more detail on usage.

How it works

The methods exported by Node’s fs module delegate to methods provided by the native bindings. For example, fs.open() and fs.openSync() call binding.open(). The mock-fs package implements the binding methods with an in-memory file system.

When you require('mock-fs'), an internal copy of the fs module with a settable binding is loaded. Methods from this modified module are copied to the fs module’s exports, making it so that any other modules that require('fs') get methods that are backed by a configurable binding. From then on, calls to mock() set the binding to the in-memory backed one, and calls to mock.restore() set the binding back to the original, native implementation.

Related Posts (see all)

Topology Preserving Simplification Comments
AngularJS Whitespace Guide Comments
Why Pull Requests? Comments