【问题标题】:Test that a function awaits something before doing anything else在执行其他任何操作之前测试一个函数是否等待某事
【发布时间】:2018-07-17 06:49:24
【问题描述】:

假设我有一个函数执行异步操作 (doStuffAsync()),然后打算做一些其他事情 (doOtherStuff())。

doStuffAsync() 返回一个Promise

还假设一切都是可模拟的。

如何在尝试 doOtherStuff() 之前测试我的函数是否等待 doStuffAsync()

我曾想过使用resolve => setTimeout(resolve(), timeout) 模拟doStuffAsync(),但基于超时的测试看起来非常脆弱。

【问题讨论】:

  • 您想测试await 关键字/承诺吗?在我看来,你应该测试你的代码,而不是语言特性。
  • 不,我想测试我的函数在开始执行其他代码之前等待某些东西。 await doStuffAsync(); doOtherStuff(); 和简单的 doStuffAsync(); doOtherStuff(); 之间存在行为差异。我想测试一下是否有 await
  • 我确实知道存在行为差异,但我不知道您如何测试它:/ 个人而言,我不会测试它。如果你在做单元测试,你调用一个函数,你期望得到一个给定的结果。函数的内部行为与测试无关。
  • 有趣的问题 ;) 我希望我们能看到一些好的答案!
  • 我遇到了一个与不等待异步函数相关的非常实际的错误 - doOtherStuff() 导航到另一个屏幕,而我明确希望它仅在 doStuffAsync() 完成后发生。

标签: javascript unit-testing asynchronous promise async-await


【解决方案1】:

您需要doStuffAsyncdoOtherStuff 都可以访问的标志。

doStuffAsync() 中写入该标志
doOtherStuff() 中读取该标志并确定它是否被写入

类似:

var isAsyncRunning = false;
    
function doStuffAsync(){
  isAsyncRunning = true;
  new Promise(function(resolve, reject) {
    setTimeout(()=>{
      isAsyncRunning = false;
      resolve(); //irrelevant in this exercise 
    }, 1000);
  });
      
}
doStuffAsync(); 
function doOtherStuff(){
  if(isAsyncRunning){
    console.log("Async is running.");
  } else {
    console.log("Async is no longer running.");
  };

}
doOtherStuff();
setTimeout(() => {
  //calling doOtherStuff 2 seconds later..
  doOtherStuff();
}, 2000);

【讨论】:

  • 请也提供测试,我认为这是主要的有趣点。我想我不明白你的意思。
  • 我认为你被 unit-testing"test that function awaits" 措辞吓到了。函数 "await" 意味着它被调用的事实。要测试这一点,您可以检查我正在谈论的标志
【解决方案2】:

我设法用比setTimeout - setImmediate 更丑的解决方案完成了它。

function testedFunction() {
    await MyModule.doStuffAsync();
    MyModule.doOtherStuff();
}

it('awaits the asynchronous stuff before doing anything else', () => {
    // Mock doStuffAsync() so that the promise is resolved at the end
    // of the event loop – which means, after the test. 
    // -
    const doStuffAsyncMock = jest.fn();
    const delayedPromise = new Promise<void>(resolve => setImmediate(resolve()));
    doStuffAsyncMock.mockImplementation(() => delayedPromise);

    const doOtherStuffMock = jest.fn();

    MyModule.doStuffAsync = doStuffAsyncMock;
    MyModule.doOtherStuffMock = doOtherStuffMock;

    testedFunction();

    expect(doOtherStuffMock).toHaveBeenCalledTimes(0);
}

setImmediate 会将您的承诺的解决推迟到事件循环结束时,即在您的测试完成之后。

因此,您断言 doOtherStuff() 未被调用:

  • 如果testedFunction 内有await 将通过
  • 如果没有就会失败。

【讨论】:

    猜你喜欢
    • 2021-01-07
    • 1970-01-01
    • 1970-01-01
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    • 2017-02-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多