21

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:

  • I'm trying to mock an error in the request. From node manual: https://nodejs.org/api/http.html#http_http_request_options_callback If any error is encountered during the request (be that with DNS resolution, TCP level errors, or actual HTTP parse errors) an 'error' event is emitted on the returned request object
  • An error code (e.g. 4xx) doesn't define the err variable. I'm trying to mock exactly that, whatever error that defines the err variable and evaluates to true
Umur Kontacı
  • 35,099
  • 7
  • 69
  • 94
coolxeo
  • 513
  • 1
  • 3
  • 10
  • can't you just make a bad request? like setting url to `''` or `undefined` for instance. – agconti Dec 30 '14 at 16:28
  • @agconti with nock you mock the server response, I'm trying to force an http error. From node manual (http://nodejs.org/api/http.html#http_http_request_options_callback) _If any error is encountered during the request (be that with DNS resolution, TCP level errors, or actual HTTP parse errors) an 'error' event is emitted on the returned request object._ – coolxeo Dec 30 '14 at 16:30
  • That behavior is irrelevant to the mock-up. Have you tried just having it reply with an HTTP error message? – SE_net4 the downvoter Dec 30 '14 at 16:49
  • @E_net4 Yes, an http error message goes in res.statusCode, it doesn't generate a javascript error – coolxeo Dec 30 '14 at 17:04
  • Right, don't mind my last comment. HTTP error messages wouldn't trigger that kind of error. But please state what kind of error you are trying to mock. – SE_net4 the downvoter Dec 30 '14 at 17:11
  • @E_net4 I'm trying to achieve 100% test coverage, so I don't mine, only err need to be defined – coolxeo Dec 30 '14 at 17:14

4 Answers4

35

Use replyWithError. From the docs:

    nock('http://www.google.com')
   .get('/cat-poems')
   .replyWithError('something awful happened');
  • nice way but coud be limited because it emits an error on the request object, not the response. But if you want to get response with error code you could use `.get('/foo') .reply(function (url, body, cb) { cb(null, [400, {}]); });` – ada May 02 '18 at 18:40
5

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.

Robert Rossmann
  • 11,141
  • 4
  • 40
  • 66
  • Very smart implementation @RobertRossman . The only problem I have is the refactoring is not easy to allow this. There is multiple requests and I need to test all of them. From the test is difficult to access this emitter instances. This is because I'm using Nock, that mocks the http requests that match certain url – coolxeo Jan 04 '15 at 12:43
  • I am not sure if there is much more you could do... There is one more option that just popped in my head - I will update this answer in a moment. – Robert Rossmann Jan 04 '15 at 13:29
  • Updated - please see the **EDIT** part. – Robert Rossmann Jan 04 '15 at 13:35
  • Thanks for your update. I think is the only thing I can do for now. Is like create my own nock, but only for that special cases. I will try if is the only alternative :) – coolxeo Jan 06 '15 at 22:38
0

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.

bluecollarcoder
  • 13,409
  • 4
  • 18
  • 18
-1

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
}
Fernando Cea
  • 227
  • 5
  • 5
  • Thanks I'm trying to achieve 100% test coverage, but I need a way to fire (provoke) that error, the problem is nock only fails if they don't have a mapping for that url – coolxeo Dec 31 '14 at 15:35