如何使用 Nock 测试请求中的错误?

Posted

技术标签:

【中文标题】如何使用 Nock 测试请求中的错误?【英文标题】:How to test error in request with Nock? 【发布时间】:2015-02-26 19:48:24 【问题描述】:

我想测试请求返回中的错误。我在测试中使用了 nock,如何强制 Nock 引发错误?我想达到 100% 的测试覆盖率并且需要为此测试 err 分支

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

永远不要进入 if err 分支。即使 hit err 是一个有效的响应,我在测试中的 Nock 行看起来像这样

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

编辑: 感谢一些 cmets:

我试图模拟请求中的错误。从节点手册: https://nodejs.org/api/http.html#http_http_request_options_callback 如果在请求期间遇到任何错误(可能是 DNS 解析、TCP 级别错误或实际的 HTTP 解析错误),则在返回的请求对象上会发出“错误”事件 错误代码(例如 4xx)未定义 err 变量。我试图准确地模拟这一点,无论定义 err 变量并评估为 true 的错误

【问题讨论】:

你就不能提出一个错误的要求吗?例如将 url 设置为 ''undefined @agconti with nock 你模拟了服务器响应,我试图强制一个 http 错误。来自节点手册 (nodejs.org/api/http.html#http_http_request_options_callback) 如果在请求期间遇到任何错误(可能是 DNS 解析、TCP 级别错误或实际 HTTP 解析错误),则会在返回的请求对象上发出“错误”事件。 这种行为与模型无关。您是否尝试过让它回复 HTTP 错误消息? @E_net4 是的,res.statusCode 中有一条 http 错误消息,它不会生成 javascript 错误 好吧,别介意我最后的评论。 HTTP 错误消息不会触发这种错误。但请说明您要模拟的错误类型。 【参考方案1】:

使用replyWithError。 来自文档:

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

【讨论】:

不错的方法,但可能会受到限制,因为它会在请求对象上发出错误,而不是响应。但是如果你想得到错误代码的响应,你可以使用.get('/foo') .reply(function (url, body, cb) cb(null, [400, ]); );【参考方案2】:

当您使用 request(url, callback) 初始化一个 http(s) 请求时,它会返回一个事件发射器实例(以及一些自定义属性/方法)。

只要你能得到这个对象(这可能需要一些重构,或者它甚至可能不适合你)你可以让这个发射器发出一个error事件,从而用@触发你的回调987654323@ 是您发出的错误。

以下代码 sn-p 演示了这一点。

'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'))
)

编辑

这种方法的问题在于,在大多数情况下,它需要进行一些重构。另一种方法利用 Node 的本地模块在整个应用程序中缓存和重用的事实,因此我们可以修改 http 模块,Request 将看到我们的修改。诀窍在于猴子修补http.request() 方法并将我们自己的逻辑注入其中。

以下代码 sn-p 演示了这一点。

'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...?
)

我怀疑 Nock 做了类似的事情(替换 http 模块上的方法),所以我建议您之后应用这个猴子补丁你已经需要(也许还配置了?)Nock

请注意,您的任务是确保仅在请求正确的 URL 时发出错误(检查 opts 对象)并恢复原始的 http.request() 实现,以便将来的测试不受您的影响变化。

【讨论】:

非常聪明的实现@RobertRossman。我唯一的问题是重构不容易允许这样做。有多个请求,我需要测试所有请求。从测试中很难访问这个发射器实例。这是因为我使用的是 Nock,它会模拟匹配特定 url 的 http 请求 我不确定你是否还能做更多...我脑海中突然出现了一个选项 - 我稍后会更新这个答案。 已更新 - 请参阅 EDIT 部分。 感谢您的更新。我想这是我目前唯一能做的。就像创建我自己的诺克一样,但仅适用于特殊情况。如果是唯一的选择,我会尝试:)【参考方案3】:

发布使用nockrequest-promise 的更新答案。

假设您的代码像这样调用request-promise

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

你可以这样设置nock来模拟500错误:

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

您的 catch 块将记录一个 StatusCodeError 对象:


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

然后您的测试可以验证该错误对象。

【讨论】:

【参考方案4】:

看起来您正在寻找关于 nock 请求的例外情况,这可能会对您有所帮助:

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

【讨论】:

谢谢我正在尝试实现 100% 的测试覆盖率,但我需要一种方法来触发(引发)该错误,问题是 nock 只有在没有该 url 的映射时才会失败

以上是关于如何使用 Nock 测试请求中的错误?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Detox 和 Nock 模拟 API 调用

如何查看 nock 是不是匹配请求?

superagent 和 nock 如何协同工作?

nock 没有拦截我的请求

错误:Nock:与请求不匹配

Nock 不适用于同时运行的多个测试