Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 13 additions & 26 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ if (!fsName) {

/**
* Hijack the real fs module immediately so the binding can be swapped at will.
* This works as expected in cases where mock-fs is required before any other
* module that wraps fs exports.
*/
var mockFs = rewire(path.join(__dirname, '..', 'node', fsName));
var originalBinding = mockFs.__get__('binding');
Expand All @@ -36,24 +38,26 @@ function setBinding(binding, Stats) {


/**
* Override the real fs module with the given configuration. Returns a function
* that can be called to restore the original file system.
* @param {Object} config File system configuration.
* @return {function()} Function called to restore the original file system.
* Swap out the fs bindings for a mock file system.
* @param {Object} config Mock file system configuration.
*/
var exports = module.exports = function(config) {
var exports = module.exports = function mock(config) {
var system = FileSystem.create(config);
var binding = new Binding(system);
setBinding(binding, binding.Stats);
};


return function restore() {
setBinding(originalBinding, originalStats);
};
/**
* Restore the fs bindings for the real file system.
*/
exports.restore = function() {
setBinding(originalBinding, originalStats);
};


/**
* Create a new fs module based on the given file system configuration.
* Create a mock fs module based on the given file system configuration.
* @param {Object} config File system configuration.
* @return {Object} A fs module with a mock file system.
*/
Expand All @@ -68,27 +72,10 @@ exports.fs = function(config) {
// overwrite fs.Stats from original binding
mockFs.Stats = binding.Stats;

// provide a method to reconfigure the file system
mockFs._reconfigure = function(opt_config) {
var newConfig = opt_config || config;
var newSystem = FileSystem.create(newConfig);
binding.setSystem(newSystem);
};

return mockFs;
};


/**
* Initialize (or reinitialize) a file system.
* @param {Object} fs A mock fs module.
* @param {Object=} opt_config File system configuration.
*/
exports.init = function(fs, opt_config) {
fs._reconfigure(opt_config);
};


/**
* Create a file factory.
*/
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mock-fs",
"description": "Mock fs implementation for testing",
"version": "1.3.1",
"description": "A configurable mock file system. You know, for testing.",
"version": "2.0.0-rc.1",
"main": "lib/index.js",
"homepage": "https://github.com/tschaub/mock-fs",
"author": {
Expand All @@ -12,7 +12,9 @@
"mock",
"fs",
"test",
"fixtures"
"fixtures",
"file system",
"memory"
],
"repository": {
"type": "git",
Expand Down
112 changes: 91 additions & 21 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# `mock-fs`

A configurable mock file system. You know, for testing.
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.

## example
## Example

The code below makes it so the `fs` module is temporarily backed by a mock file system with a few files and directories.

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

var restore = mock({
mock({
'path/to/fake/dir': {
'some-file.txt': 'file content here',
'empty-dir': {/** empty directory */}
Expand All @@ -19,26 +19,47 @@ var restore = mock({
});
```

Note that the `mock` function returns a `restore` function. When you are ready to restore the `fs` module (so that it is backed by your real file system), call `restore()`.
When you are ready to restore the `fs` module (so that it is backed by your real file system), call [`mock.restore()`](#mockrestore).

```js
// after a test runs
restore();
mock.restore();
```

## Docs

### <a id='mockconfig'>`mock(config)`</a>

Configure the `fs` module so it is backed by an in-memory file system.

Calling `mock` sets up a mock file system with at least two directories: `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.

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.

*Note that paths should always use forward slashes (`/`) - even on Windows.*

### Creating files

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).
```js
mock({
'path/to/file.txt': 'file content here'
});
```

## docs
To create a file with additional properties (owner, permissions, atime, etc.), use the [`mock.file()`](#mockfileproperties) function described below.

### `mock.file(properties)`
### <a id='mockfileproperties'>`mock.file(properties)`</a>

Create a factory for new files. Supported properties:

* **content** - `string|Buffer` File contents.
* **mode** - `number` File mode (permission and sticky bits). Defaults to `0666`.
* **uid** - `number` The user id. Defaults to `process.getuid()`.
* **git** - `number` The group id. Defaults to `process.getgid()`.
* **atime** - `Date` The last file access time.
* **ctime** - `Date` The last file change time.
* **mtime** - `Date` The last file modification time.
* **atime** - `Date` The last file access time. Defaults to `Date.now()`. Updated when file contents are accessed.
* **ctime** - `Date` The last file change time. Defaults to `Date.now()`. Updated when file owner or permissions change.
* **mtime** - `Date` The last file modification time. Defaults to `Date.now()`. Updated when file contents change.

To create a mock filesystem with a very old file named `foo`, you could do something like this:
```js
Expand All @@ -51,16 +72,38 @@ mock({
});
```

### `mock.directory(properties)`
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()`.

### Creating directories

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):
```js
// note that this could also be written as
// mock({'path/to/dir': { /** config */ }})
mock({
path: {
to: {
dir: {
file1: 'text content',
file2: new Buffer([1, 2, 3, 4])
}
}
}
});
```

To create a directory with additional properties (owner, permissions, atime, etc.), use the [`mock.directory()`](mockdirectoryproperties) function described below.

### <a id='mockdirectoryproperties'>`mock.directory(properties)`</a>

Create a factory for new directories. Supported properties:

* **mode** - `number` Directory mode (permission and sticky bits). Defaults to `0777`.
* **uid** - `number` The user id. Defaults to `process.getuid()`.
* **git** - `number` The group id. Defaults to `process.getgid()`.
* **atime** - `Date` The last directory access time.
* **ctime** - `Date` The last directory change time.
* **mtime** - `Date` The last directory modification time.
* **atime** - `Date` The last directory access time. Defaults to `Date.now()`.
* **ctime** - `Date` The last directory change time. Defaults to `Date.now()`. Updated when owner or permissions change.
* **mtime** - `Date` The last directory modification time. Defaults to `Date.now()`.
* **items** - `Object` Directory contents. Members will generate additional files, directories, or symlinks.

To create a mock filesystem with a directory with the relative path `some/dir` that has a mode of `0755` and a couple child files, you could do something like this:
Expand All @@ -76,17 +119,23 @@ mock({
});
```

### `mock.symlink(properties)`
Note that if you want to create a directory with the default properties, you can provide an `Object` directly instead of calling `mock.directory()`.

### Creating symlinks

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.

### <a id='mocksymlinkproperties'>`mock.symlink(properties)`</a>

Create a factory for new symlinks. Supported properties:

* **path** - `string` Path to the source (required).
* **mode** - `number` Symlink mode (permission and sticky bits). Defaults to `0666`.
* **uid** - `number` The user id. Defaults to `process.getuid()`.
* **git** - `number` The group id. Defaults to `process.getgid()`.
* **atime** - `Date` The last symlink access time.
* **ctime** - `Date` The last symlink change time.
* **mtime** - `Date` The last symlink modification time.
* **atime** - `Date` The last symlink access time. Defaults to `Date.now()`.
* **ctime** - `Date` The last symlink change time. Defaults to `Date.now()`.
* **mtime** - `Date` The last symlink modification time. Defaults to `Date.now()`.

To create a mock filesystem with a file and a symlink, you could do something like this:
```js
Expand All @@ -100,19 +149,40 @@ mock({
});
```

## install
### Restoring the file system

### <a id='mockrestore'>`mock.restore()`</a>

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:

```js
beforeEach(function() {
mock({
'fake-file': 'file contents'
});
});
afterEach(mock.restore);
```

### Creating a new `fs` module instead of modifying the original

### <a id='mockfsconfig'>`mock.fs(config)`</a>

Calling `mock()` modifies Node's built-in `fs` module. This is useful when you want to test with a mock file system. If for some reason you want to work with the real file system and an in-memory version at the same time, you can call the `mock.fs()` function. This takes the same `config` object [described above](#mockconfig) and sets up a in-memory file system. Instead of modifying the binding for the built-in `fs` module (as is done when calling `mock(config)`), the `mock.fs(config)` function returns an object with the same interface as the `fs` module, but backed by your mock file system.

## Install

Using `npm`:

```
npm install mock-fs --save-dev
```

## caveats
## Caveats

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.

The following `fs` functions are overridden: `fs.ReadStream`, `fs.Stats`, `fs.WriteStream`, `fs.appendFile`, `fs.appendFileSync`, `fs.chmod`, `fs.chmodSync`, `fs.chown`, `fs.chownSync`, `fs.close`, `fs.closeSync`, `fs.createReadStream`, `fs.createWriteStream`, `fs.exists`, `fs.existsSync`, `fs.fchmod`, `fs.fchmodSync`, `fs.fchown`, `fs.fchownSync`, `fs.fdatasync`, `fs.fdatasyncSync`, `fs.fstat`, `fs.fstatSync`, `fs.fsync`, `fs.fsyncSync`, `fs.ftruncate`, `fs.ftruncateSync`, `fs.futimes`, `fs.futimesSync`, `fs.lchmod`, `fs.lchmodSync`, `fs.lchown`, `fs.lchownSync`, `fs.link`, `fs.linkSync`, `fs.lstatSync`, `fs.lstat`, `fs.mkdir`, `fs.mkdirSync`, `fs.open`, `fs.openSync`, `fs.read`, `fs.readSync`, `fs.readFile`, `fs.readFileSync`, `fs.readdir`, `fs.readdirSync`, `fs.readlink`, `fs.readlinkSync`, `fs.realpath`, `fs.realpathSync`, `fs.rename`, `fs.renameSync`, `fs.rmdir`, `fs.rmdirSync`, `fs.stat`, `fs.statSync`, `fs.symlink`, `fs.symlinkSync`, `fs.truncate`, `fs.truncateSync`, `fs.unlink`, `fs.unlinkSync`, `fs.utimes`, `fs.utimesSync`, `fs.write`, `fs.writeSync`, `fs.writeFile`, and `fs.writeFileSync`.
The following [`fs` functions](http://nodejs.org/api/fs.html) are overridden: `fs.ReadStream`, `fs.Stats`, `fs.WriteStream`, `fs.appendFile`, `fs.appendFileSync`, `fs.chmod`, `fs.chmodSync`, `fs.chown`, `fs.chownSync`, `fs.close`, `fs.closeSync`, `fs.createReadStream`, `fs.createWriteStream`, `fs.exists`, `fs.existsSync`, `fs.fchmod`, `fs.fchmodSync`, `fs.fchown`, `fs.fchownSync`, `fs.fdatasync`, `fs.fdatasyncSync`, `fs.fstat`, `fs.fstatSync`, `fs.fsync`, `fs.fsyncSync`, `fs.ftruncate`, `fs.ftruncateSync`, `fs.futimes`, `fs.futimesSync`, `fs.lchmod`, `fs.lchmodSync`, `fs.lchown`, `fs.lchownSync`, `fs.link`, `fs.linkSync`, `fs.lstatSync`, `fs.lstat`, `fs.mkdir`, `fs.mkdirSync`, `fs.open`, `fs.openSync`, `fs.read`, `fs.readSync`, `fs.readFile`, `fs.readFileSync`, `fs.readdir`, `fs.readdirSync`, `fs.readlink`, `fs.readlinkSync`, `fs.realpath`, `fs.realpathSync`, `fs.rename`, `fs.renameSync`, `fs.rmdir`, `fs.rmdirSync`, `fs.stat`, `fs.statSync`, `fs.symlink`, `fs.symlinkSync`, `fs.truncate`, `fs.truncateSync`, `fs.unlink`, `fs.unlinkSync`, `fs.utimes`, `fs.utimesSync`, `fs.write`, `fs.writeSync`, `fs.writeFile`, and `fs.writeFileSync`.

Mock `fs.Stats` objects have the following properties: `dev`, `ino`, `nlink`, `mode`, `size`, `rdev`, `blksize`, `blocks`, `atime`, `ctime`, `mtime`, `uid`, and `gid`. In addition, all of the `is*()` method are provided (e.g. `isDirectory()`, `isFile()`, et al.).

Expand Down
7 changes: 2 additions & 5 deletions test/integration/filecount.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ var count = require('./filecount');

describe('count(dir, callback)', function() {

var restore;
beforeEach(function() {
restore = mock({
mock({
'path/to/dir': {
'one.txt': 'first file',
'two.txt': 'second file',
Expand All @@ -18,9 +17,7 @@ describe('count(dir, callback)', function() {
}
});
});
afterEach(function() {
restore();
});
afterEach(mock.restore);

it('counts files in a directory', function(done) {

Expand Down
Loading