【问题标题】:Promise not rejecting correctly in Jest test承诺在 Jest 测试中没有正确拒绝
【发布时间】:2020-07-06 08:44:16
【问题描述】:

当承诺拒绝使用 Jest 时,我正在尝试测试 console.error 输出。我发现在我的测试运行后承诺似乎正在解决,导致测试失败。

示例函数:

export default function doSomething({ getData }) {

  const success = data => {
    //do stuff with the data
  }
  const handleError = () => {
    //handle the error
  }

  getData.then(response => success(response)).catch(error => {
    console.error(error)
    handleError()
  })

}

示例测试文件:

import doSomething from "doSomething"

it("should log console.error if the promise is rejected", async () => {
  const getData = new Promise((resolve, reject) => {
    reject("fail");
  });
  global.console.error = jest.fn();
  await doSomething({ getData });
  expect(global.console.error).toHaveBeenCalledWith("fail");
})
//fails with global.console.error has not been called

当我探索这个问题时,我注意到如果我添加一个 console.log 并等待它,它就可以工作。

这会过去的...

import doSomething from "doSomething"

it("should log console.error if the promise is rejected", async () => {
  const getData = new Promise((resolve, reject) => {
    reject("fail");
  });
  global.console.error = jest.fn();
  await doSomething({ getData });
  await console.log("anything here");
  expect(global.console.error).toHaveBeenCalledWith("fail");
})

如何正确测试这个?我应该重构getData 函数的调用方式吗?只要调用doSomething 函数,就需要调用它。

【问题讨论】:

    标签: javascript promise jestjs


    【解决方案1】:

    为什么原来的测试失败了?

    了解为什么第一个测试示例无法通过的诀窍在于深入了解 await 运算符的实际操作。来自Mozilla docs

    [rv] = await expression;
    
    • expression - Promise 或任何等待的值。
    • rv - 返回 promise 的已实现值,如果不是 Promise,则返回值本身。

    在您的第一个测试中,expression 的值是doSomething 函数的返回值。您不会从此函数返回任何内容,因此返回值将是 undefined。这不是 Promise,所以 await 无事可做,它只会返回 undefined 并继续前进。然后expect 语句将失败,因为您实际上并没有等待内部承诺:getData.then(...).catch(...)

    要修复测试,无需添加额外的行 await console.log("anything here");,只需 return 来自 doSomething 函数的内部承诺,以便 await 运算符实际操作 Promise。

    export default function doSomething({ getData }) {
      return getData.then(...).catch(...);
      ...
    }
    

    这是测试这个的正确方法吗?

    我认为doSomething 函数的编写方式没有什么大问题。这种依赖注入通常使函数比试图模拟函数的内部工作更容易测试。

    我只能认识到,因为您正在注入一个 Promise (getData),并在函数中解析它,所以您已经使 doSomething 函数异步(这使得测试变得更加复杂)。

    如果您解析了 Promise,然后在它解析为 getData.then(doSomething).catch(handleError) 的值上调用 doSomething,那么您的 doSomething 函数将是同步的并且更易于测试。我还要说,以这种方式编写它会使异步发生的事情变得更加冗长,而原始 doSomething({ getData }) 将其隐藏在 doSomething 函数体中。

    所以没有什么完全不正确的,但也许需要考虑一些事情,这可能会使测试更容易,代码更冗长。希望对您有所帮助!

    【讨论】:

    • 感谢您的解释。缺少的 return 语句是一个简单但非常重要的步骤。您的解决方案效果很好。
    猜你喜欢
    • 2018-11-22
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2015-10-28
    • 2018-02-25
    • 2015-06-04
    • 1970-01-01
    • 2017-07-16
    相关资源
    最近更新 更多