Automatically record and playback HTTP calls for each tape test. This package is really just a decorator that wraps each test individual test with nock's Nock Back feature. This helps avoid the all nockBack
wrapping code which can make tests less clear.
npm install tape-nock --save-dev
var path = require('path')
var tape = require('tape') // still need to require tape
var tapeNock = require('tape-nock')
// call tapeNock with tape and an options object
var test = tapeNock(tape, { //options object to be passed to nock, not required
fixtures: path.join(__dirname, 'fixtures'), // this is the default path
mode: 'dryrun' // this is the default mode
defaultTestOptions: { // optionally provide default options to nockBack for each test
before: function () {
console.log('a preprocessing function, gets called before nock.define')
},
after: function () {
console.log('a postprocessing function, gets called after nock.define')
}
}
})
(see nockBack test options for more information on before, after, and afterRecord functions used in defaultTestOptions
)
Now just write your tape tests as usual:
var request = require('request')
test('do it live on the internet', function(t) {
request.get('https://registry.npmjs.org', function (err, res) {
t.error(err)
t.ok(res)
t.end()
})
in dryrun
mode, the above test will go to the internet and nothing will be recorded.
Once your tests are perfected, Create fixture files by using the record
mode. This captures all HTTP calls per test and saves it to the fixtures
directory.
To record, set the NOCK_BACK_MODE
environment variable:
NOCK_BACK_MODE=record npm test
...or do it programatically via the tape-nock
options object:
var test = tapeNock(tape, {
fixtures: path.join(__dirname, 'fixtures'),
mode: 'record' // record mode!
})
A new file called do it live on the internet.json
will be created in the fixtures
directory. It will contain an array of HTTP calls that were made during the test. Each test will have its own JSON.
Once a fixture exists for a test, it will be used every time in dryrun
mode. To re-record, you'll need to delete the JSON file.
To make this easier, add scripts to your package.json
for easy recording/running:
{
"scripts" {
"test": "tape test/*js"
"test:record": "NOCK_BACK_MODE=record npm test",
"test:wild": "NOCK_BACK_MODE=wild npm test",
"test:lockdown": "NOCK_BACK_MODE=lockdown npm test",
"test:overwrite": "rm test/fixtures/*.json & npm run test:record"
}
}
It is also possible to manually mock at the same time so a request NEVER hits a URL.
Just get a copy of nock from tape-nock
via .nock
and use it:
var tape = require('tape')
var tapeNock = require('tape-nock')
var test = tapeNock(tape, {
fixtures: path.join(__dirname, 'fixtures') //defaults to this path
})
var request = require('request')
test('able to get a copy of nock from test.nock and use it', function (t) {
// get a copy of nock
var nock = test.nock
// use it to mock a URL. This mock will live even if NOCK_BACK_MODE=wild
nock('http://registry.npmjs.org').get('/clockmoji').reply(200, {'yep': 'it works'})
request.get('http://registry.npmjs.org/clockmoji', process)
function process (err, resp) {
t.error(err, 'no error')
t.equals(JSON.parse(resp.body).yep, 'it works', 'able to mock directly with nock instance')
t.end()
}
})
Use the NOCK_BACK_MODE
environment variable (details) to control the mode of nockBack.
Here is a recap
- wild: all requests go out to the internet, don't replay anything, doesn't record anything
- dryrun: The default, use recorded nocks, allow http calls, doesn't record anything, useful for writing new tests
- record: use recorded nocks, record new nocks
- lockdown: use recorded nocks, disables all http calls even when not nocked, doesn't record
Its also possible to pass nockBack options through tape's options object.
This is helpful for doing filteringPath
or filteringRequestBody
(check out the PROTIP... that can be done in an "after" function).
For Example, this test will use the after
function passed in, which will make all "time-based-param" params in the path and replace them with 123. This will ensure time-based parameters will still be mock-able (the path in the fixture JSON will need to be manually updated to match).
var after = function (scope) {
scope.filteringPath(/time-based-param=[^&]*/g, 'time-based-param=123')
}
test('pass through opts to nockback', {after: after}, function (t) {
request.get('http://registry.npmjs.com?time-based-param=1455231758348', function (err, resp) {
t.error(err)
t.equals(JSON.parse(resp.body).haha, 'no secrets for you', 'secrets are protected')
t.end()
})
})
Since nockBack will mock all HTTP requests, using supertest can be tricky. Here is an example of how to avoid mocking/recording local connections when using supertest
.
Here is our application. It simply hits http://httpbin.org/get which echos info back. We want to have nockBack record/mock the httpbin request but still allow supertest
http requests to 127.0.0.1 to pass through for all tests. This is done by leveraging the defaultTestOptions
.
app.js
const express = require('express')
const request = require('superagent')
var app = express()
app.get('/myapp/version', function (req, res) {
superagent
.get('http://httpbin.org/get')
.end(function (err, response) {
res.status(200).json({
version: '0.1.0',
url: response.body.url
})
})
})
module.exports = app
test.js
const supertest = require('supertest');
const app = require('../app.js');
const tape = require('tape');
const tapeNock = require('tape-nock');
const nock = tapeNock.nock;
const opts = {
// after recording the fixtures, remove any scopes that hit 127.0.0.1
// this is not necessary with our before function below, but it makes it a bit cleaner.
afterRecord: function (scopes) {
var localhost = /http:\/\/127\.0\.0\.1.*/;
scopes = scopes.filter(function (s) {
return !localhost.test(s.scope);
});
return scopes;
},
before: function () {
// allow connections to 127.0.0.1 even when NOCK_BACK_MODE=lockdown
nock.enableNetConnect('127.0.0.1');
}
};
// call tapeNock with tape and an options object
const test = tapeNock(tape, { defaultTestOptions: opts });
// note that we're passing in test.options here
// which has our special "afterRecord" and "before" functions
test('hit version url', function (t) {
supertest(app)
.get('/myapp/version')
.expect(200, {
url: 'http://httpbin.org/get',
version: '0.1.0'
})
.end(function (err, res) {
t.error(err, 'no error');
t.equals(res.body.url, 'http://httpbin.org/get', 'url is correct');
t.end();
});
});
Contributions welcome! Please read the contributing guidelines first.