Javascript 在玩笑测试中未正确拒绝的承诺

Javascript 在玩笑测试中未正确拒绝的承诺,javascript,promise,jestjs,Javascript,Promise,Jestjs,我正在尝试测试控制台。当承诺使用Jest拒绝时,会出现错误输出。我发现在我的测试运行后,承诺似乎正在解决,导致测试失败 示例函数: export default function doSomething({ getData }) { const success = data => { //do stuff with the data } const handleError = () => { //handle the error } getDa

我正在尝试测试
控制台。当承诺使用Jest拒绝时,会出现错误
输出。我发现在我的测试运行后,承诺似乎正在解决,导致测试失败

示例函数:

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
函数,就需要调用它

为什么原始测试失败?

理解为什么第一个测试示例无法通过的诀窍在于深入了解
await
操作符实际在做什么。从:

  • 表达式
    -承诺或任何需要等待的值
  • rv
    -返回承诺的已实现值,如果不是承诺,则返回值本身
在第一次测试中,
表达式
的值是
doSomething
函数的返回值。您没有从该函数返回任何内容,因此返回值将是
未定义的
。这不是一个承诺,因此
无需等待
执行,它只会返回
未定义
并继续。然后,
expect
语句将失败,因为您实际上没有等待内部承诺:
getData.then(…).catch(…)

要修复测试,无需添加额外的行,
wait console.log(“此处的任何内容”)
,只需
返回
doSomething
函数的内部承诺,这样
wait
操作符将实际操作该承诺

export default function doSomething({ getData }) {
  return getData.then(...).catch(...);
  ...
}
这是正确的测试方法吗?

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

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

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


因此,没有什么是绝对错误的,但可能需要考虑一些事情,这可能会使测试更容易,代码更冗长。我希望这有帮助

谢谢你的解释。缺少return语句是一个简单但非常重要的步骤。你的解决方案很有效。
export default function doSomething({ getData }) {
  return getData.then(...).catch(...);
  ...
}