An easy-to-use mock for child_process.spawn.
- All mock processes wrap a user-supplied, asynchronous "runner function" that calls a callback to indicate that the process is "done running"
- You can plug in a "strategy function" that returns runner functions for handling specific spawn invocations.
- When no specific runner function is found, a default function is used. The baked-in default returns an exit code of 0 and returns immediately. The default function may be overridden for fancier behavior.
- All the information on how many times
spawnwas invoked and the details of every invocation are available as attributes on the mock for later assertions - Testing simple cases requires no setup whatsoever
- A simple "sequence" strategy for being able to say: do this on the first invocation, do the other thing on the second invocation and so on is available for use
var mockSpawn = require('mock-spawn');
// override child_process.spawn
// this is a simplistic example; you can use a library like `mockery` to
// set a new instance for every test. See examples/complete/test.js
var mySpawn = mockSpawn();
require('child_process').spawn = mySpawn;
// at this point you have mocked child_process.spawn to always exit 0
// and write nothing to stdout or stderr
// let's change the default processing to exit 1 always and write something
// to stdout
mySpawn.setDefault(mySpawn.simple(1 /* exit code */, 'hello world' /* stdout */));
// let's tell the mock to do specific things on sequential calls
// in this case we exit 0 on the first call, 1 on the second call and so on
mySpawn.sequence.add(mySpawn.simple(0));
mySpawn.sequence.add(mySpawn.simple(1));
mySpawn.sequence.add(function (cb) {
setTimeout(function () { return cb(2); }, 2000);
});
mySpawn.sequence.add(function (cb) {
// test the error handling of your library
this.emit('error', new Error('spawn ENOENT'));
setTimeout(function() { return cb(8); }, 10);
});
mySpawn.sequence.add({throws:new Error('spawn ENOENT')});
// the fourth call to spawn will use the default function we set up to exit 1
// the fifth call to spawn will emit an error and emit exit with code 8 on the
// next tick of the event loop
// the sixth call to spawn will throw an error synchronously
// call your test library here that invokes spawn the way you expect it to
lib.doSomething(function (err) {
/* after the test is done running, you can make assertions like so */
assert.equal(6, mySpawn.calls.length);
var firstCall = mySpawn.calls[0];
assert.equal('ls', firstCall.command);
assert.deepEqual([ '-l' ], firstCall.args);
assert.equal(0, firstCall.exitCode);
});var mockSpawn = require('mock-spawn');
// basic stuff
var mySpawn = mockSpawn();
require('child_process').spawn = mySpawn;
// we are now testing if our library under test retries spawn commands on error
// when executing the `foo` command
var count = 0;
mySpawn.setStrategy(function (command, args, opts) {
if (command !== 'foo') { return null; } // use default
if (++count < 3) {
return mySpawn.simple(1); //exit 1 immediately
}
return function (cb) {
this.stdout.write('output data my library expects');
return cb(0); // and exit 0
};
});The runner function accepts a single callback that needs to be called with an
exit code and optionally a signal name. If you define a throws property on
the runner object, it will throw that error synchronously to mimic the
behavior of child_process.spawn. It will ignore everything else in this case,
and it will not "run" at all.
CAVEAT: The throws value must be an instanceof Error.
The runner function has access to the following attributes via this
this.stdout- the standard output of the process to which it can writethis.stderr- the standard error of the process to which it can writethis.command- the command for thespawncallthis.args- the args for thespawncallthis.opts- the options object passed to thespawncallthis.emit- the emit method of the underlyingEventEmitter
The process "runs" until the runner calls the callback.
The strategy function accepts 3 arguments: the command, args and options passed
to the spawn invocation. It can inspect these to return a customized runner
for just that invocation.
It can also return a falsy value to indicate that the default function should be used.
returns a function that can be plugged into child_process as a replacement
for spawn
- verbose - true to see additional debug messages from this library
returns a runner function that exits with the specified code and writes specific data to the output and error streams
Arguments are:
- exit-code: exit code for the process
- output-data: the data to be written to standard output
- error-data: the data to be written to standard error
sets the default processing of all spawn invocations to use the runner function specified
- fn - a runner function to handler default processing
enables the sequence strategy and calls the runner function supplied at the
specific point in the sequence.
- fn - the runner function to use. The nth call to
addplugs a runner function for the nth invocation tospawn
Do not mix sequence.add and setStrategy calls for a specific run.
sets fn as the strategy that will return runner functions on demand.
- fn - the function to be used as the strategy function.
Do not mix sequence.add and setStrategy calls for a specific run.
sets obj as a lookup table for whether to exit. If the value is true,
then the runner will emit exit with code null and signal <signal>.
- obj - the object with signal names and whether to exit.
array of mock process objects that you can use to inspect how your library
under test invoked spawn. Every object has the following properties available
command- the commandargs- the command argumentsopts- the options passed to the spawn invocationexitCode- the exit code of the processsignal- the signal delivered to the process (simulated via the runner)
BSD. See accompanying LICENSE file.
The following third-party libraries are used by this module:
Pull requests welcome!
child_process.forkandchild_process.execprocessing- strategy functions on
process.kill
