coolxeo
coolxeo

Reputation: 563

How to test error in request with Nock?

I want to test the error in a request return. I'm using nock in my tests, how can I force Nock to provoke an error? I want to achieve 100% test coverage and need to test err branch for that

request('/foo', function(err, res) {
  if(err) console.log('boom!');
});

Never enter in the if err branch. Even if hit err is a valid response, my Nock line in test looks like this

nock('http://localhost:3000').get('/foo').reply(400);

edit: thanks to some comments:

Upvotes: 26

Views: 25018

Answers (4)

bluecollarcoder
bluecollarcoder

Reputation: 14399

Posting an updated answer for using nock with request-promise.

Let's assume that your code calls request-promise like this:

require('request-promise')
  .get({
    url: 'https://google.com/'
  })
  .catch(res => {
    console.error(res);
  });

you can set up nock like this to simulate a 500 error:

nock('https://google.com')
  .get('/')
  .reply(500, 'FAILED!');

Your catch block would log a StatusCodeError object:

{
  name: 'StatusCodeError',
  statusCode: 500,
  message: '500 - "FAILED!"',
  error: 'FAILED!',
  options: {...},
  response: {
    body: 'FAILED!',
    ...
  }
}

Your test can then validate that error object.

Upvotes: 1

Krzysiek Szlapinski
Krzysiek Szlapinski

Reputation: 451

Use replyWithError. From the docs:

    nock('http://www.google.com')
   .get('/cat-poems')
   .replyWithError('something awful happened');

Upvotes: 45

Robert Rossmann
Robert Rossmann

Reputation: 12129

When you initialise a http(s) request with request(url, callback), it returns an event emitter instance (along with some custom properties/methods).

As long as you can get your hands on this object (this might require some refactoring or perhaps it might not even be suitable for you) you can make this emitter to emit an error event, thus firing your callback with err being the error you emitted.

The following code snippet demonstrates this.

'use strict';

// Just importing the module
var request = require('request')
// google is now an event emitter that we can emit from!
  , google = request('http://google.com', function (err, res) {
      console.log(err) // Guess what this will be...?
    })

// In the next tick, make the emitter emit an error event
// which will trigger the above callback with err being
// our Error object.
process.nextTick(function () {
  google.emit('error', new Error('test'))
})

EDIT

The problem with this approach is that it, in most situations, requires a bit of refactoring. An alternative approach exploits the fact that Node's native modules are cached and reused across the whole application, thus we can modify the http module and Request will see our modifications. The trick is in monkey-patching the http.request() method and injecting our own bit of logic into it.

The following code snippet demonstrates this.

'use strict';

// Just importing the module
var request = require('request')
  , http = require('http')
  , httpRequest = http.request

// Monkey-patch the http.request method with
// our implementation
http.request = function (opts, cb) {
  console.log('ping');
  // Call the original implementation of http.request()
  var req = httpRequest(opts, cb)

  // In next tick, simulate an error in the http module
  process.nextTick(function () {
    req.emit('error', new Error('you shall not pass!'))
    // Prevent Request from waiting for
    // this request to finish
    req.removeAllListeners('response')
    // Properly close the current request
    req.end()
  })

  // We must return this value to keep it
  // consistent with original implementation
  return req
}

request('http://google.com', function (err) {
  console.log(err) // Guess what this will be...?
})

I suspect that Nock does something similar (replacing methods on the http module) so I recommend that you apply this monkey-patch after you have required (and perhaps also configured?) Nock.

Note that it will be your task to make sure you emit the error only when the correct URL is requested (inspecting the opts object) and to restore the original http.request() implementation so that future tests are not affected by your changes.

Upvotes: 5

Fernando Cea
Fernando Cea

Reputation: 267

Looks like you're looking for an exception on a nock request, this maybe can help you:

var nock = require('nock');
var google = nock('http://google.com')
               .get('/')
               .reply(200, 'Hello from Google!');

try{
  google.done();
}
catch (e) {
  console.log('boom! -> ' + e); // pass exception object to error handler
}

Upvotes: 0

Related Questions