回调函数的嵌套_如何对嵌套的回调函数进行单元测试

回调函数的嵌套

Writing unit tests can be fun but also tricky, especially if you’re testing code that may not have been written with unit testing in mind.

编写单元测试可能很有趣,但也很棘手,特别是如果您要测试的代码可能没有考虑到单元测试的情况。

One scenario that I keep encountering in various ways — my own unit testing tasks, teammates at work asking “How do I test this?”, this StackOverflow question — is how to unit test a module or function that has business logic contained within a callback function being invoked by a nested entity that needs to be mocked for the test.

我一直以各种方式遇到的一个场景-我自己的单元测试任务,工作中的队友问“我该如何测试?”, 这个StackOverflow问题 -是如何对在回调中包含业务逻辑的模块或函数进行单元测试由嵌套实体调用的函数,需要对该实体进行模拟以进行测试。

场景 (The Scenario)

Here’s an example of app code I’m talking about (Node app code, in this case) that we’d like to write unit tests for:

这是我正在谈论的应用程序代码示例(在本例中为Node应用程序代码),我们将为此编写单元测试:

module.exports = (filepath) => {
  return new Promise((resolve, reject) => {
    fs.readFile(filepath, (err, data) => {
      if (err) {
        reject(err);
        return;
      }


      const parsed = JSON.parse(data);


      if (parsed.success) {
        resolve(parsed);
      } else {
        reject({ dataError: true });
      }
    });
  });
};

(This is a simplified example — I’ve seen callback functions that go for miles.)

(这是一个简化的示例,我已经看到了许多回调函数。)

The unit we’re testing here is our customworklist module, which is wrapping the Node fs module in a Promise. We want to add some unit tests for the business logic contained in the callback function being passed to fs.readFile.

我们在此处测试的单元是自定义worklist模块,该模块将Node fs模块包装在Promise中。 我们要为传递给fs.readFile的回调函数中包含的业务逻辑添加一些单元测试。

However, that callback gets invoked by fs after its file read is complete, and since we’ll be mocking fs for our unit tests, how do we test that code?

但是,该回调在文件读取完成后由fs调用,并且由于我们将模拟fs进行单元测试,因此如何测试该代码?

不同的看法 (A Different View)

I think what can help is to remember that that passed-in callback is just another argument being passed to the readFile method, like a string or a number. So if, in your mind (I’m not suggesting you modify the app code), you replace that noisy inline function with a simple function reference, it can help clarify what’s going on:

我认为可以帮助您记住的是,传入的回调只是传递给readFile方法的另一个参数,例如字符串或数字。 因此,如果您想到了(我不建议您修改应用程序代码),而用一个简单的函数引用替换了该嘈杂的内联函数,那么它将有助于弄清发生了什么:

module.exports = (filepath) => {
  return new Promise((resolve, reject) => {
    fs.readFile(filepath, callback); // <-- replace inline function with function ref
  });
};

So now you can see that if we mock fs.readFile, our mock will receive the argumentsfilepath and callback, so you can visualize the app code like this:

因此,现在您可以看到,如果我们模拟fs.readFile ,我们的模拟将接收参数filepathcallback ,因此您可以像这样可视化应用程序代码:

module.exports = (filepath) => {
  return new Promise((resolve, reject) => {
    fsMock.readFile(filepath, callback); // <-- our mock readFile is passed the callback
  });
};

最终测试 (The Final Test)

Since our mock readFile method is receiving that callback as a parameter, in our mock we can invoke the callback with whatever err and data arguments we choose in order to test our different scenarios.

由于我们的模拟readFile方法正在接收该回调作为参数,因此在我们的模拟中,我们可以使用选择的任何errdata参数调用该回调,以测试我们的不同方案。

For example, here’s what the test for a file read error would look like (this is using Mocha, Chai, and rewire, but the approach is not library-specific):

例如,这是文件读取错误测试的样子(这是使用MochaChairewire ,但是这种方法不是特定于库的):

it('rejects if error on file read', () => {
  const errorResponse = { fileError: true };
  const fsMock = {
    readFile: (path, callback) => {
      callback(errorResponse, null); // <--- manually invoke nested callback
    }
  };


  worklist.__set__('fs', fsMock);


  return worklist('myfile.json').then((res) => {
  }, (err) => {
    expect(err).to.equal(errorResponse);
  });
});

When worklist() is called, the Promise contructor is called, which immediately invokesfs.readFile, which is actually our mock’s readFile method.

调用worklist() ,将调用Promise构造函数,该构造函数立即调用fs.readFile ,这实际上是我们的模拟程序的readFile方法。

In our mock, manually invoking the callback with our desired arguments for this particular test exercises the business logic and causes the Promise to reject, which we expect inside the error handler on line 13.

在我们的模拟中,针对此特定测试使用所需参数来手动调用回调将行使业务逻辑,并导致Promise拒绝,这是我们expect在第13行的错误处理程序内。

离别的想法 (Parting Thoughts)

Writing unit tests can be challenging sometimes, especially for legacy code written by developers who were clearly unburdened by the question “How will I unit test this?”

编写单元测试有时可能会充满挑战,尤其是对于那些显然没有“如何进行单元测试?”这个问题的开发人员编写的遗留代码。

I generally find the challenging cases enjoyable, though, because it becomes like solving a puzzle. And when you eventually figure out a solution, it’s much more rewarding than just another straightforward test you’ve written hundreds of times before.

不过,我通常认为具有挑战性的案例很有趣,因为它变得像解决难题一样。 当您最终找到解决方案时,它比您之前编写数百次的另一种简单测试更有意义。

翻译自: https://medium.com/@michaeljjacobson/how-to-unit-test-a-nested-callback-function-649f4ea68698

回调函数的嵌套

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值