Katcha Katcha - 7 days ago 7
Node.js Question

Mocking NodeJS request and response with Nock

I'm trying to get the grasp of the tool Nock in order to mock the request and response from my code doing the calls. I'm using npm request as a simple HTTP client to request the back-end REST API, Chai for the expectation library and Mocha to run my tests. Here is the code that I have for the tests:

var nock = require('nock');
var storyController = require('../modules/storyController');

var getIssueResponse = {
//JSON object that we expect from the API response.
}

it('It should get the issue JSON response', function(done) {
nock('https://username.atlassian.net')
.get('/rest/api/latest/issue/AL-6')
.reply(200, getIssueResponse);

storyController.getStoryStatus("AL-6", function(error, issueResponse) {
var jsonResponse = JSON.parse(issueResponse);

expect(jsonResponse).to.be.a('object');
done();
})
});


And here is the code to do the GET request:

function getStoryStatus(storyTicketNumber, callback) {
https.get('https://username.atlassian.net/rest/api/latest/issue/' + storyTicketNumber, function (res) {

res.on('data', function(data) {
callback(null, data.toString());
});

res.on('error', function(error) {
callback(error);
});
})
}


This single test is passing and I don't understand why. It seems like it is actually doing a real call and not using my fake nock request/response. If I comment the nock section or change:

.reply(200, getIssueResponse) to .reply(404)


It doesn't break the test and nothing change, I'm not doing anything with my nock variable. Can someone please explain me with a clear example how to mock the request and response in my NodeJS http-client using Nock?

Answer

TLDR: I think your code is doing more than what it tells you.

Important note: when putting an http request in "stream mode" the data event could (and probably does) gets fired multiple times, each one for a "chunk" of data, over internet chunks could be variable between 1400 to 64000 bytes, so expect multiple callback invocations (that's a very special kind of bad)

As a simple suggestion, you can try using request or just concatenate the received data, then invoke the callback on the end event.

I have tried a very small snippet using the latter technique

var assert = require('assert');
var https = require('https');
var nock = require('nock');

function externalService(callback) {
  // directly from node documentation:
  // https://nodejs.org/api/https.html#https_https_get_options_callback
  https.get('https://encrypted.google.com/', (res) => {

    var data = '';
    res.on('data', (d) => {
      data += d;
    });

    res.on('end', () => callback(null, JSON.parse(data)));
  // on request departure error (network is down?)
  // just invoke callback with first argument the error
  }).on('error', callback);
}


describe('Learning nock', () => {
  it('should intercept an https call', (done) => {
    var bogusMessage = 'this is not google, RLY!';

    var intercept = nock('https://encrypted.google.com').get('/')
      .reply(200, { message: bogusMessage });

    externalService((err, googleMessage) => {
      if (err) return done(err);

      assert.ok(intercept.isDone());
      assert.ok(googleMessage);
      assert.strictEqual(googleMessage.message, bogusMessage);

      done();
    });

  })
})

And it works very fine, even with using on('data')

Edit:

Reference on how to handle correctly a stream

I have expanded the example as a full-fledged mocha example