unexpected-fs

3.0.0 • Public • Published

unexpected-fs

Mock filesystem for your assertions.

Unexpected file system :)

NPM version Build Status Coverage Status Dependency Status

Be aware that when you mock out the file system it will have side effects for the require function. require uses fs internally, and thus you will not be able to load modules from your local file system if you mount a mock-fs that shadows that location. This problem is mitigated by the fact that the mock file system will only exist while your assertion is running.

Usage

The unexpected-fs plugin adds the 'with fs mocked out' assertion to the assertion framework unexpected. You can mix it into any sequence of assertions, and it will patch the fs module for the assertions following it.

Take the following example:

var expect = require('unexpected');
 
var fs = require('fs');
 
function fileContentCAPS(filename) {
    var fileContent = fs.readFileSync('/data/' + filename, 'utf-8');
    return fileContent.toUpperCase();
}
 
expect('foobar.txt', 'when passed as parameter to', fileContentCAPS,
       'to equal', 'HELLO WORLD');

For that test to pass, you have to place fixtures on the file system. That poses two problems:

  • You remove data out of the context of the test.
  • Your tests become reliant on the file system.

In the following example, we mount a mocked fs on the path /data/ with a file named foobar.txt.

var expect = require('unexpected')
    .clone()
    .installPlugin(require('unexpected-fs');
 
var fs = require('fs');
 
function fileContentCAPS(filename) {
    var fileContent = fs.readFileSync('/data/' + filename, 'utf-8');
    return fileContent.toUpperCase();
}
 
expect('foobar.txt', 'with fs mocked out', {
    '/data': {
        'foobar.txt': 'Hello world',
    }
}, 'when passed as parameter to', fileContentCAPS, 'to equal', 'HELLO WORLD');

See the test suite in express-jsxtransform for a real life example of how it simplify your tests.

Assertions

with fs mocked out

Mocking out the filesystem for the rest of the rest of the duration of the given call to unexpected.

function fileContentCAPS(filename) {
    var fileContent = fs.readFileSync(filename, 'utf-8');
    return fileContent.toUpperCase();
}
 
expect('/data/foobar.txt', 'with fs mocked out', {
    '/data': {
        'foobar.txt': 'Hello world',
    }
}, 'when passed as parameter to', fileContentCAPS, 'to equal', 'HELLO WORLD');

to be a (path|text file) satisfying

Assert that the stats of a given path, or the stats, and the content, of a given path to a text file, satisfies the given object.

expect('/path/to/file', 'to be a text file satisfying', {
    ctime: new Date('Sun Jun 14 2015 23:40:01 GMT+0200'),
    content: 'the content as a utf-8 string'
});

The instance methods from fs.Stats, isFile, isDirectory, isSymbolicLink, isBlockDevice, isCharacterDevice, isFIFO and isSocket, is all made available as booleans on the stats object which is 'to satisfied' against.

expect('/path/to/directory', 'to be a path satisfying', {
    isDirectory: true,
    mode: 16877
});

<string> [not] to be an existing path

Asserts that the given string is a path that exists on the file system, or not.

expect('/path/that/does/not/exist', 'not to be an existing path');
expect('/', 'to be an existing path');

mock-fs and mountfs

This module is just a custom assertion that uses two other modules to mock out the filesystem for the duration of the expect call.

mock-fs is used to create the file system it self, and mountfs to mount those filesystems on top of the real filesystem.

mountfs is used to avoid altering more than necessary. If you only used mock-fs, you would change fs entirely for the entire process, which would mean that you could not test code that relied on lazy loading of modules through require for example.

How mock-fs is used

To make mock-fs work for us in this context I had to depart from their API on a few ways:

The mock.file() helper method would not be available as the user should not be required to do more than just install the unexpected-fs plugin. Using mock fs you would call with an options object, like so:

mock.file({
    ctime: new Date(112432332),
    content: 'foobar'
});

You can do the same with unexpected-fs, by just passing the options object but adding a property called _isFile with the value of true.

{
    _isFile: true,
    ctime: new Date(112432332),
    content: 'foobar'
}

Before passing the arguments on to mock-fs, the _isFile property will be removed and the object will be passed to mock.file.

The same is true for the mock.directory and mock.symlink methods, and the corresponding properties is called _isDirectory and _isSymlink.

Another difference is a consequence of how mountfs is added to the mix. mock-fs would normally overwrite the entire global fs module, and you would not be able to read from the already existing file system. That is solved by only mocking out part of the file system, as given by the mountPath. Say that we want to mock out a file called journal.txt in the folder /home/john/notes.

it('should be able to read the contents of a file', function () {
    return expect(function () {
        var fs = require('fs');
        var fileContent = fs.readFileSync('/home/john/notes/journal.txt', 'utf-8');
        return expect(fileContent, 'to equal', 'foo bar');
    }, 'with fs mocked out', {
        '/home/john/notes': {
            'journal.txt': 'foo bar'
        }
    }, 'not to throw');
});

If the original file system had data in /home/john/notes/ those data will now be hidden by our mock fs. If the folders /home, or /home/john, or /home/john/notes did not exist on the original file system, they will appear to do now.

You are not able to mock out files, without mounting a mock-fs first. So the following example will NOT work:

expect(..., 'with fs mocked out', {
    '/path/to/file.txt': 'blah'
}, ...);

While it could be convenient to do it like the above example, it is a tradeoff which enables us to mount multiple small mocked file systems instead of having to override everything at once. Each key, on the root level of the configuration object will be it's own little mock fs. Consider this example:

expect(..., 'with fs mocked out', {
    '/home/john/notes': { ... },
    '/home/john/documents': { ... }
}, ...);

That allows us to mock out both folders mentioned in the object, while still being able to read stuff in the folders outside of the mounted mock file systems.

License

This module is made public under the ISC License.

See the LICENSE file for additional details.

Package Sidebar

Install

npm i unexpected-fs

Weekly Downloads

8

Version

3.0.0

License

ISC

Unpacked Size

40.8 kB

Total Files

14

Last publish

Collaborators

  • munter
  • joelmukuthu
  • gustavnikolaj
  • alexjeffburke
  • papandreou
  • sunesimonsen