NMock is an HTTP mocking and expectations library for Node.js
NMock is a fork of Nock
NMock can be used to test modules that perform HTTP requests in isolation.
For instance, if a module performs HTTP requests to a CouchDB server or makes HTTP requests to the Amazon API, you can test that module in isolation.
Table of Contents
- Install
- Use
- READ THIS! - About interceptors
- Specifying hostname
- Specifying path
- Specifying request body
- Specifying request query string
- Specifying replies
- Specifying headers
- HTTP Verbs
- Support for HTTP and HTTPS
- Non-standard ports
- Repeat response n times
- Delay the response body
- Delay the response
- Delay the connection
- Socket timeout
- Chaining
- Scope filtering
- Path filtering
- Request Body filtering
- Request Headers Matching
- Allow unmocked requests on a mocked hostname
- Expectations
- Logging
- Restoring
- Turning NMock Off (experimental!)
- Enable/Disable real HTTP request
- Recording
- Events
- NMock Back
- How does it work?
- Debugging
- PROTIP
- Generate Changelog
- License
$ npm install nmock
On your test, you can setup your mocking object like this:
var nmock = require('nmock');
var couchdb = nmock('http://myapp.iriscouch.com')
.get('/users/1')
.reply(200, {
_id: '123ABC',
_rev: '946B7D1C',
username: 'pgte',
email: 'pedro.teixeira@gmail.com'
});
This setup says that we will intercept every HTTP call to http://myapp.iriscouch.com
.
It will intercept an HTTP GET request to '/users/1' and reply with a status 200, and the body will contain a user representation in JSON.
Then the test can call the module, and the module will do the HTTP requests.
When you setup an interceptor for an URL and that interceptor is used, it is removed from the interceptor list. This means that you can intercept 2 or more calls to the same URL and return different things on each of them. It also means that you must setup one interceptor for each request you are going to have, otherwise nmock will throw an error because that URL was not present in the interceptor list.
The request hostname can be a string or a RegExp.
var scope = nmock('http://www.example.com')
.get('/resource')
.reply(200, 'domain matched');
var scope = nmock(/example\.com/)
.get('/resource')
.reply(200, 'domain regex matched');
(You can choose to include or not the protocol in the hostname matching)
The request path can be a string, a RegExp or a filter function and you can use any HTTP verb.
Using a string:
var scope = nmock('http://www.example.com')
.get('/resource')
.reply(200, 'path matched');
Using a regular expression:
var scope = nmock('http://www.example.com')
.get(/source$/)
.reply(200, 'path using regex matched');
Using a function:
var scope = nmock('http://www.example.com')
.get(function(uri) {
return uri.indexOf('cats') >= 0;
})
.reply(200, 'path using function matched');
You can specify the request body to be matched as the second argument to the get
, post
, put
or delete
specifications like this:
var scope = nmock('http://myapp.iriscouch.com')
.post('/users', {
username: 'pgte',
email: 'pedro.teixeira@gmail.com'
})
.reply(201, {
ok: true,
id: '123ABC',
rev: '946B7D1C'
});
The request body can be a string, a RegExp, a JSON object or a function.
var scope = nmock('http://myapp.iriscouch.com')
.post('/users', /email=.?@gmail.com/gi)
.reply(201, {
ok: true,
id: '123ABC',
rev: '946B7D1C'
});
If the request body is a JSON object, a RegExp can be used to match an attribute value.
var scope = nmock('http://myapp.iriscouch.com')
.post('/users', {
username: 'pgte',
password: /a.+/,
email: 'pedro.teixeira@gmail.com'
})
.reply(201, {
ok: true,
id: '123ABC',
rev: '946B7D1C'
});
If the request body is a function, return true if it should be considered a match:
var scope = nmock('http://myapp.iriscouch.com')
.post('/users', function(body) {
return body.id === '123ABC';
})
.reply(201, {
ok: true,
id: '123ABC',
rev: '946B7D1C'
});
NMock understands query strings. Instead of placing the entire URL, you can specify the query part as an object:
nmock('http://example.com')
.get('/users')
.query({name: 'pedro', surname: 'teixeira'})
.reply(200, {results: [{id: 'pgte'}]});
NMock supports array-style/object-style query parameters. The encoding format matches with request module.
nmock('http://example.com')
.get('/users')
.query({
names: ['alice', 'bob'],
tags: {
alice: ['admin', 'tester'],
bob: ['tester']
}
})
.reply(200, {results: [{id: 'pgte'}]});
NMock supports passing a function to query. The function determines if the actual query matches or not.
nmock('http://example.com')
.get('/users')
.query(function(actualQueryObject){
// do some compare with the actual Query Object
// return true for matched
// return false for not matched
return true;
})
.reply(200, {results: [{id: 'pgte'}]});
To mock the entire url regardless of the passed query string:
nmock('http://example.com')
.get('/users')
.query(true)
.reply(200, {results: [{id: 'pgte'}]});
You can specify the return status code for a path on the first argument of reply like this:
var scope = nmock('http://myapp.iriscouch.com')
.get('/users/1')
.reply(404);
You can also specify the reply body as a string:
var scope = nmock('http://www.google.com')
.get('/')
.reply(200, 'Hello from Google!');
or as a JSON-encoded object:
var scope = nmock('http://myapp.iriscouch.com')
.get('/')
.reply(200, {
username: 'pgte',
email: 'pedro.teixeira@gmail.com',
_id: '4324243fsd'
});
or even as a file:
var scope = nmock('http://myapp.iriscouch.com')
.get('/')
.replyWithFile(200, __dirname + '/replies/user.json');
Instead of an object or a buffer you can also pass in a callback to be evaluated for the value of the response body:
var scope = nmock('http://www.google.com')
.filteringRequestBody(/.*/, '*')
.post('/echo', '*')
.reply(201, function(uri, requestBody) {
return requestBody;
});
An asynchronous function that gets an error-first callback as last argument also works:
var scope = nmock('http://www.google.com')
.filteringRequestBody(/.*/, '*')
.post('/echo', '*')
.reply(201, function(uri, requestBody, cb) {
fs.readFile('cat-poems.txt' , cb); // Error-first callback
});
Note: When using a callback, if you call back with an error as first argument, that error will be sent in the response body, with a 500 HTTP response status code.
You can also return the status code and body using just one function:
var scope = nmock('http://www.google.com')
.filteringRequestBody(/.*/, '*')
.post('/echo', '*')
.reply(function(uri, requestBody) {
return [
201,
'THIS IS THE REPLY BODY',
{'header': 'value'} // optional headers
];
});
or, use an error-first callback that also gets the status code:
var scope = nmock('http://www.google.com')
.filteringRequestBody(/.*/, '*')
.post('/echo', '*')
.reply(function(uri, requestBody, cb) {
setTimeout(function() {
cb(null, [201, 'THIS IS THE REPLY BODY'])
}, 1e3);
});
A Stream works too:
var scope = nmock('http://www.google.com')
.get('/cat-poems')
.reply(200, function(uri, requestBody) {
return fs.createReadStream('cat-poems.txt');
});
If you're using the reply callback style, you can access the original client request using this.req
like this:
var scope = nmock('http://www.google.com')
.get('/cat-poems')
.reply(function(uri, requestBody) {
console.log('path:', this.req.path);
console.log('headers:', this.req.headers);
// ...
});
You can reply with an error like this:
nmock('http://www.google.com')
.get('/cat-poems')
.replyWithError('something awful happened');
JSON error responses are allowed too:
nmock('http://www.google.com')
.get('/cat-poems')
.replyWithError({'message': 'something awful happened', 'code': 'AWFUL_ERROR'});
NOTE: This will emit an
error
event on therequest
object, not the reply.
Per HTTP/1.1 4.2 Message Headers specification, all message headers are case insensitive and thus internally NMock uses lower-case for all field names even if some other combination of cases was specified either in mocking specification or in mocked requests themselves.
You can specify the request headers like this:
var scope = nmock('http://www.example.com', {
reqheaders: {
'authorization': 'Basic Auth'
}
})
.get('/')
.reply(200);
Or you can use a Regular Expression or Function check the header values. The function will be passed the header value.
var scope = nmock('http://www.example.com', {
reqheaders: {
'X-My-Headers': function (headerValue) {
if (headerValue) {
return true;
}
return false;
},
'X-My-Awesome-Header': /Awesome/i
}
})
.get('/')
.reply(200);
If reqheaders
is not specified or if host
is not part of it, NMock will automatically add host
value to request header.
If no request headers are specified for mocking then NMock will automatically skip matching of request headers. Since host
header is a special case which may get automatically inserted by NMock, its matching is skipped unless it was also specified in the request being mocked.
You can also have NMock fail the request if certain headers are present:
var scope = nmock('http://www.example.com', {
badheaders: ['cookie', 'x-forwarded-for']
})
.get('/')
.reply(200);
When invoked with this option, NMock will not match the request if any of the badheaders
are present.
Basic authentication can be specified as follows:
var scope = nmock('http://www.example.com')
.get('/')
.basicAuth({
user: 'john',
pass: 'doe'
})
.reply(200);
You can specify the reply headers like this:
var scope = nmock('http://www.headdy.com')
.get('/')
.reply(200, 'Hello World!', {
'X-My-Headers': 'My Header value'
});
Or you can use a function to generate the headers values. The function will be passed the request, response, and body (if available). The body will be either a buffer, a stream, or undefined.
var scope = nmock('http://www.headdy.com')
.get('/')
.reply(200, 'Hello World!', {
'X-My-Headers': function (req, res, body) {
return body.toString();
}
});
You can also specify default reply headers for all responses like this:
var scope = nmock('http://www.headdy.com')
.defaultReplyHeaders({
'X-Powered-By': 'Rails',
'Content-Type': 'application/json'
})
.get('/')
.reply(200, 'The default headers should come too');
Or you can use a function to generate the default headers values:
var scope = nmock('http://www.headdy.com')
.defaultReplyHeaders({
'Content-Length': function (req, res, body) {
return body.length;
}
})
.get('/')
.reply(200, 'The default headers should come too');
When using scope.reply()
to set a response body manually, you can have the
Content-Length
header calculated automatically.
var scope = nmock('http://www.headdy.com')
.replyContentLength()
.get('/')
.reply(200, { hello: 'world' });
NOTE: this does not work with streams or other advanced means of specifying the reply body.
You can automatically append a Date
header to your mock reply:
var scope = nmock('http://www.headdy.com')
.replyDate(new Date(2015, 0, 1)) // defaults to now, must use a Date object
.get('/')
.reply(200, { hello: 'world' });
NMock supports any HTTP verb, and it has convenience methods for the GET, POST, PUT, HEAD, DELETE, PATCH and MERGE HTTP verbs.
You can intercept any HTTP verb using .intercept(path, verb [, requestBody [, options]])
:
scope('http://my.domain.com')
.intercept('/path', 'PATCH')
.reply(304);
By default nmock assumes HTTP. If you need to use HTTPS you can specify the https://
prefix like this:
var scope = nmock('https://secure.my.server.com')
// ...
You are able to specify a non-standard port like this:
var scope = nmock('http://my.server.com:8081')
...
You are able to specify the number of times to repeat the same response.
nmock('http://zombo.com').get('/').times(4).reply(200, 'Ok');
http.get('http://zombo.com/'); // respond body "Ok"
http.get('http://zombo.com/'); // respond body "Ok"
http.get('http://zombo.com/'); // respond body "Ok"
http.get('http://zombo.com/'); // respond body "Ok"
http.get('http://zombo.com/'); // respond with zombo.com result
Sugar syntax
nmock('http://zombo.com').get('/').once().reply(200, 'Ok');
nmock('http://zombo.com').get('/').twice().reply(200, 'Ok');
nmock('http://zombo.com').get('/').thrice().reply(200, 'Ok');
You are able to specify the number of milliseconds that the response body should be delayed. Response header will be replied immediately.
delayBody(1000)
is equivalent to delay({body: 1000})
.
nmock('http://my.server.com')
.get('/')
.delayBody(2000) // 2 seconds
.reply(200, '<html></html>')
NOTE: the 'response'
event will occur immediately, but the IncomingMessage not emit it's 'end'
event until after the delay.
You are able to specify the number of milliseconds that your reply should be delayed.
nmock('http://my.server.com')
.get('/')
.delay(2000) // 2 seconds delay will be applied to the response header.
.reply(200, '<html></html>')
delay()
could also be used as
delay({
head: headDelayInMs,
body: bodyDelayInMs
}
for example
nmock('http://my.server.com')
.get('/')
.delay({
head: 2000, // header will be delayed for 2 seconds, i.e. the whole response will be delayed for 2 seconds.
body: 3000 // body will be delayed for another 3 seconds after header is sent out.
})
.reply(200, '<html></html>')
delayConnection(1000)
is equivalent to delay({head: 1000})
.
You are able to specify the number of milliseconds that your connection should be idle, to simulate a socket timeout.
nmock('http://my.server.com')
.get('/')
.socketDelay(2000) // 2 seconds
.reply(200, '<html></html>')
To test a request like the following:
req = http.request('http://my.server.com', function(res) {
...
});
req.setTimeout(1000, function() {
req.abort();
});
req.end();
NOTE: the timeout will be fired immediately, and will not leave the simulated connection idle for the specified period of time.
You can chain behaviour like this:
var scope = nmock('http://myapp.iriscouch.com')
.get('/users/1')
.reply(404)
.post('/users', {
username: 'pgte',
email: 'pedro.teixeira@gmail.com'
})
.reply(201, {
ok: true,
id: '123ABC',
rev: '946B7D1C'
})
.get('/users/123ABC')
.reply(200, {
_id: '123ABC',
_rev: '946B7D1C',
username: 'pgte',
email: 'pedro.teixeira@gmail.com'
});
You can filter the scope (protocol, domain and port through) of a nmock through a function. This filtering functions is defined at the moment of defining the nmock's scope through its optional options
parameters:
This can be useful, for instance, if you have a node module that randomly changes subdomains to which it sends requests (e.g. Dropbox node module is like that)
var scope = nmock('https://api.dropbox.com', {
filteringScope: function(scope) {
return /^https:\/\/api[0-9]*.dropbox.com/.test(scope);
}
})
.get('/1/metadata/auto/Photos?include_deleted=false&list=true')
.reply(200);
You can also filter the URLs based on a function.
This can be useful, for instance, if you have random or time-dependent data in your URL.
You can use a regexp for replacement, just like String.prototype.replace:
var scope = nmock('http://api.myservice.com')
.filteringPath(/password=[^&]*/g, 'password=XXX')
.get('/users/1?password=XXX')
.reply(200, 'user');
Or you can use a function:
var scope = nmock('http://api.myservice.com')
.filteringPath(function(path) {
return '/ABC';
})
.get('/ABC')
.reply(200, 'user');
Note that scope.filteringPath
is not cumulative: it should only be used once per scope.
You can also filter the request body based on a function.
This can be useful, for instance, if you have random or time-dependent data in your request body.
You can use a regexp for replacement, just like String.prototype.replace:
var scope = nmock('http://api.myservice.com')
.filteringRequestBody(/password=[^&]*/g, 'password=XXX')
.post('/users/1', 'data=ABC&password=XXX')
.reply(201, 'OK');
Or you can use a function to transform the body:
var scope = nmock('http://api.myservice.com')
.filteringRequestBody(function(body) {
return 'ABC';
})
.post('/', 'ABC')
.reply(201, 'OK');
If you need to match requests only if certain request headers match, you can.
var scope = nmock('http://api.myservice.com')
.matchHeader('accept', 'application/json')
.get('/')
.reply(200, {
data: 'hello world'
})
You can also use a regexp for the header body.
var scope = nmock('http://api.myservice.com')
.matchHeader('User-Agent', /Mozilla\/.*/)
.get('/')
.reply(200, {
data: 'hello world'
})
You can also use a function for the header body.
var scope = nmock('http://api.myservice.com')
.matchHeader('content-length', function (val) {
return val >= 1000;
})
.get('/')
.reply(200, {
data: 'hello world'
})
If you need some request on the same host name to be mocked and some others to really go through the HTTP stack, you can use the allowUnmocked
option like this:
options = {allowUnmocked: true};
var scope = nmock('http://my.existing.service.com', options)
.get('/my/url')
.reply(200, 'OK!');
// GET /my/url => goes through nmock
// GET /other/url => actually makes request to the server
Bear in mind that, when applying
{allowUnmocked: true}
if the request is made to the real server, no interceptor is removed.
Every time an HTTP request is performed for a scope that is mocked, NMock expects to find a handler for it. If it doesn't, it will throw an error.
Calls to nmock() return a scope which you can assert by calling scope.done()
. This will assert that all specified calls on that scope were performed.
Example:
var google = nmock('http://google.com')
.get('/')
.reply(200, 'Hello from Google!');
// do some stuff
setTimeout(function() {
google.done(); // will throw an assertion error if meanwhile a "GET http://google.com" was not performed.
}, 5000);
You can call isDone()
on a single expectation to determine if the expectation was met:
var scope = nmock('http://google.com')
.get('/')
.reply(200);
scope.isDone(); // will return false
It is also available in the global scope, which will determine if all expectations have been met:
nmock.isDone();
You can cleanup all the prepared mocks (could be useful to cleanup some state after a failed test) like this:
nmock.cleanAll();
You can make all the interceptors for a scope persist by calling .persist()
on it:
var scope = nmock('http://persisssists.con')
.persist()
.get('/')
.reply(200, 'Persisting all the way');
Note that while a persisted scope will always intercept the requests, it is considered "done" after the first interception.
If a scope is not done, you can inspect the scope to infer which ones are still pending using the scope.pendingMocks()
function:
if (!scope.isDone()) {
console.error('pending mocks: %j', scope.pendingMocks());
}
It is also available in the global scope:
console.error('pending mocks: %j', nmock.pendingMocks());
NMock can log matches if you pass in a log function like this:
var google = nmock('http://google.com')
.log(console.log)
...
You can restore the HTTP interceptor to the normal unmocked behaviour by calling:
nmock.restore();
note: restore does not clear the interceptor list. Use nmock.cleanAll() if you expect the interceptor list to be empty.
You can bypass NMock completely by setting NMOCK_OFF
environment variable to "true"
.
This way you can have your tests hit the real servers just by switching on this environment variable.
$ NMOCK_OFF=true node my_test.js
As default, if you do not mock a host, a real HTTP request will do, but sometimes you should not permit real HTTP request, so...
For disabling real http requests.
nmock.disableNetConnect();
So, if you try to request any host not 'nmocked', it will thrown an NetConnectNotAllowedError
.
nmock.disableNetConnect();
var req = http.get('http://google.com/');
req.on('error', function(err){
console.log(err);
});
// The returned `http.ClientRequest` will emit an error event (or throw if you're not listening for it)
// This code will log a NetConnectNotAllowedError with message:
// NMock: Not allow net connect for "google.com:80"
For enabling real HTTP requests (the default behaviour).
nmock.enableNetConnect();
You could allow real HTTP request for certain host names by providing a string or a regular expression for the hostname:
// using a string
nmock.enableNetConnect('amazon.com');
// or a RegExp
nmock.enableNetConnect(/(amazon|github).com/);
http.get('http://www.amazon.com/');
http.get('http://github.com/'); // only for second example
// This request will be done!
http.get('http://google.com/');
// this will throw NetConnectNotAllowedError with message:
// NMock: Not allow net connect for "google.com:80"
A common use case when testing local endpoints would be to disable all but local host, then adding in additional nmocks for external requests:
nmock.disableNetConnect();
nmock.enableNetConnect('127.0.0.1'); //Allow localhost connections so we can test local routes and mock servers.
Then when you're done with the test, you probably want to set everything back to normal:
nmock.cleanAll();
nmock.enableNetConnect();
This is a cool feature:
Guessing what the HTTP calls are is a mess, specially if you are introducing nmock on your already-coded tests.
For these cases where you want to mock an existing live system you can record and playback the HTTP calls like this:
nmock.recorder.rec();
// Some HTTP calls happen and the nmock code necessary to mock
// those calls will be outputted to console
Recording relies on intercepting real requests and answers and then persisting them for later use.
ATTENTION!: when recording is enabled, nmock does no validation.
If you just want to capture the generated code into a var as an array you can use:
nmock.recorder.rec({
dont_print: true
});
// ... some HTTP calls
var nmockCalls = nmock.recorder.play();
The nmockCalls
var will contain an array of strings representing the generated code you need.
Copy and paste that code into your tests, customize at will, and you're done!
(Remember that you should do this one test at a time).
In case you want to generate the code yourself or use the test data in some other way, you can pass the output_objects
option to rec
:
nmock.recorder.rec({
output_objects: true
});
// ... some HTTP calls
var nmockCallObjects = nmock.recorder.play();
The returned call objects have the following properties:
scope
- the scope of the call including the protocol and non-standard ports (e.g.'https://github.com:12345'
)method
- the HTTP verb of the call (e.g.'GET'
)path
- the path of the call (e.g.'/pgte/nmock'
)body
- the body of the call, if anystatus
- the HTTP status of the reply (e.g.200
)response
- the body of the reply which can be a JSON, string, hex string representing binary buffers or an array of such hex strings (when handlingcontent-encoded
in reply header)headers
- the headers of the replyreqheader
- the headers of the request
If you save this as a JSON file, you can load them directly through nmock.load(path)
. Then you can post-process them before using them in the tests for example to add them request body filtering (shown here fixing timestamps to match the ones captured during recording):
nmocks = nmock.load(pathToJson);
nmocks.forEach(function(nmock) {
nmock.filteringRequestBody = function(body, aRecordedBody) {
if (typeof(body) !== 'string' || typeof(aRecordedBody) !== 'string') {
return body;
}
var recordedBodyResult = /timestamp:([0-9]+)/.exec(aRecordedBody);
if (!recordedBodyResult) {
return body;
}
var recordedTimestamp = recordedBodyResult[1];
return body.replace(/(timestamp):([0-9]+)/g, function(match, key, value) {
return key + ':' + recordedTimestamp;
});
};
});
Alternatively, if you need to pre-process the captured nmock definitions before using them (e.g. to add scope filtering) then you can use nmock.loadDefs(path)
and nmock.define(nmockDefs)
. Shown here is scope filtering for Dropbox node module which constantly changes the subdomain to which it sends the requests:
// Pre-process the nmock definitions as scope filtering has to be defined before the nmocks are defined (due to its very hacky nature).
var nmockDefs = nmock.loadDefs(pathToJson);
nmockDefs.forEach(function(def) {
// Do something with the definition object e.g. scope filtering.
def.options = def.options || {};
def.options.filteringScope = function(scope) {
return /^https:\/\/api[0-9]*.dropbox.com/.test(scope);
};
}
// Load the nmocks from pre-processed definitions.
var nmocks = nmock.define(nmockDefs);
Recording request headers by default is deemed more trouble than its worth as some of them depend on the timestamp or other values that may change after the tests have been recorder thus leading to complex postprocessing of recorded tests. Thus by default the request headers are not recorded.
The genuine use cases for recording request headers (e.g. checking authorization) can be handled manually or by using enable_reqheaders_recording
in recorder.rec()
options.
nmock.recorder.rec({
dont_print: true,
output_objects: true,
enable_reqheaders_recording: true
});
Note that even when request headers recording is enabled NMock will never record user-agent
headers. user-agent
values change with the version of Node and underlying operating system and are thus useless for matching as all that they can indicate is that the user agent isn't the one that was used to record the tests.
NMock will print using console.log
by default (assuming that dont_print
is false
). If a different function is passed into logging
, nmock will send the log string (or object, when using output_objects
) to that function. Here's a basic example.
var appendLogToFile = function(content) {
fs.appendFile('record.txt', content);
}
nmock.recorder.rec({
logging: appendLogToFile,
});
By default, nmock will wrap it's output with the separator string <<<<<<-- cut here -->>>>>>
before and after anything it prints, whether to the console or a custom log function given with the logging
option.
To disable this, set use_separator
to false.
nmock.recorder.rec({
use_separator: false
});
This allows removing a specific interceptor. This can be either an interceptor instance or options for a url. It's useful when there's a list of common interceptors shared between tests, where an individual test requires one of the shared interceptors to behave differently.
Examples:
nmock.removeInterceptor({
hostname : 'localhost',
path : '/mockedResource'
});
nmock.removeInterceptor({
hostname : 'localhost',
path : '/login'
method: 'POST'
proto : 'https'
});
var interceptor = nmock('http://example.org')
.get('somePath');
nmock.removeInterceptor(interceptor);
A scope emits the following events:
emit('request', function(req, interceptor))
;emit('replied', function(req, interceptor, originalRequestOptions))
;
You can also listen for no match events like this:
nmock.emitter.on('no match', function(req) {
});
If NMock doesn't have an interceptor for the request and allowUnmocked
options is enabled, you can listen global no match response
event like this:
nmock.emitter.on('no match response', function(req, responseData) {
});
fixture recording support and playback
**You must specify a fixture directory before using, for example:
In your test helper
var nmockBack = require('nmock').back;
nmockBack.fixtures = '/path/to/fixtures/';
nmockBack.setMode('record');
nmockBack.fixtures
: path to fixture directorynmockBack.setMode()
: the mode to use
By default if the fixture doesn't exist, a nmockBack
will create a new fixture and save the recorded output
for you. The next time you run the test, if the fixture exists, it will be loaded in.
The this
context of the call back function will be have a property scopes
to access all of the loaded
nmock scopes
var nmockBack = require('nmock').back;
var request = require('request');
nmockBack.setMode('record');
nmockBack.fixtures = __dirname + '/nmockFixtures'; //this only needs to be set once in your test helper
var before = function(scope) {
scope.filteringRequestBody = function(body, aRecordedBody) {
if (typeof(body) !== 'string' || typeof(aRecordedBody) !== 'string') {
return body;
}
var recordedBodyResult = /timestamp:([0-9]+)/.exec(aRecodedBody);
if (!recodedBodyResult) {
return body;
}
var recordedTimestamp = recodedBodyResult[1];
return body.replace(/(timestamp):([0-9]+)/g, function(match, key, value) {
return key + ':' + recordedTimestamp;
});
};
}
// recording of the fixture
nmockBack('zomboFixture.json', function(nmockDone) {
request.get('http://zombo.com', function(err, res, body) {
nmockDone();
// usage of the created fixture
nmockBack('zomboFixture.json', function (nmockDone) {
http.get('http://zombo.com/').end(); // respond body "Ok"
this.assertScopesFinished(); //throws an exception if all nmocks in fixture were not satisfied
http.get('http://zombo.com/').end(); // throws exception because someFixture.json only had one call
nmockDone(); //never gets here
});
});
});
As an optional second parameter you can pass the following options
before
: a preprocessing function, gets called before nmock.defineafter
: a postprocessing function, gets called after nmock.defineafterRecord
: a postprocessing function, gets called after recording. Is passed the array of scopes recorded and should return the array scopes to save to the fixturerecorder
: custom options to pass to the recorder
to set the mode call nmockBack.setMode(mode)
or run the tests with the NMOCK_BACK_MODE
environment variable set before loading nmock. If the mode needs to be changed programatically, the following is valid: nmockBack.setMode(nmockBack.currentMode)
-
wild: all requests go out to the internet, don't replay anything, doesn't record anything
-
dryrun: The default, use recorded nmocks, allow http calls, doesn't record anything, useful for writing new tests
-
record: use recorded nmocks, record new nmocks
-
lockdown: use recorded nmocks, disables all http calls even when not nmocked, doesn't record
NMock works by overriding Node's http.request
function. Also, it overrides http.ClientRequest
too to cover for modules that use it directly.
NMock uses debug, so just run with environmental variable DEBUG set to nmock.*
$ DEBUG=nmock.* node my_test.js
If you don't want to match the request body you can use this trick (by @theycallmeswift):
var scope = nmock('http://api.myservice.com')
.filteringRequestBody(function(body) {
return '*';
})
.post('/some_uri', '*')
.reply(200, 'OK');
$ npm install changelog -g
$ npm run changelog
(The MIT License)
Copyright (c) 2016 Eugene Ponomarev
Copyright (c) 2011-2015 Pedro Teixeira. http://about.me/pedroteixeira
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.