期望被测函数内部模拟的解析值

分享于2023年05月01日 jestjs node.js promise unit-testing 问答
【问题标题】:Expecting the resolved value of a mock inside of a function under test期望被测函数内部模拟的解析值
【发布时间】:2023-04-30 09:49:01
【问题描述】:

请记住,我对 Jest 还很陌生,而且我对 Node 的 Promise 的内部结构并不十分熟悉。我知道 async 函数实际上是返回 Promise 的函数,我在这种想法上取得了很好的成功。

我有一个正在尝试测试的功能。它具有在执行任何其他工作之前导入的依赖项。根据传递给 foo 的输入,该依赖项可能会返回不同的内容,这些内容可能会或可能不会影响 foo 的结果。它是这样的:

// foo.js

const someOtherWork = require("../bar/baz");

module.exports = async function foo() {
  const array = await someOtherWork();
  ... do other things ...
  return someStuff;
}

我导入了一个函数来帮助完成 foo 正在尝试做的工作。

在我的测试中,我像这样监视 someOtherWork 函数:

// foo.test.js

const someOtherWork = jest.requireActual("../bar/baz");
const mockSomeOtherWork = jest.fn().mockImplementation(someOtherWork);
jest.mock("../bar/baz", () => mockSomeOtherWork);

const foo = require("./foo.js");

// ...tests...

有了这个,我可以断言 mockSomeOtherWork 是用特定参数调用的,甚至它返回了正确的输出。当我的模拟依赖项(在这种情况下为 someOtherWork )返回 Promise 时,我被绊倒了。

当我尝试断言 Promise 解析为特定值 而不在我的测试代码中调用模拟函数 时,它 总是 通过了测试: p>

expect(mockSomeOtherWork).toHaveReturnedWith(Promise.resolve([my, array, of, expected, values])); // succeeds correctly
expect(mockSomeOtherWork).toHaveReturnedWith(Promise.reject("lol nope"); // succeeds ERRONEOUSLY

当我断言它返回 Array 或其他任何东西时,我已经成功地失败了测试,如下所示:

expect(mockSomeOtherWork).toHaveReturnedWith(Array); // fails correctly
expect(mockSomeOtherWork).toHaveReturnedWith([my, array, of, expected, values]); // fails correctly

但是任何形式的 Promise 通过 测试,这意味着我不能从我的断言中假设任何内容!

我开始发现,对于 Jest, Promise 是一个非常不透明的对象,我必须 await 它或附加回调(通过 then catch )来弄清楚里面是什么. Promise 是否不记得它们已经解析的值供我稍后检查,而无需重新调用返回它的函数?我不想依赖断言 someOtherWork 的其他依赖项或 foo 的返回值,因为这些选项假设了很多关于 foo 的内容,这些内容可能随时发生变化。我在 someOtherWork 上已经有了一个有效的间谍模拟工具。我应该改为从 mockSomeOtherWork.mock.results[0].value await 中获取返回的 Promise 吗?是否有一种“正确”的方式来断言被测单元的依赖关系的 async 结果?


【解决方案1】:

这是我的解决方法:

// If all is well, this should be a Promise
const results = mockSomeOtherWork.mock.results[0].value;

// If that Promise resolves to the right stuff, gucci!
const expected = [my, array, of, expected, values];
await expect(results).resolves.toStrictEqual(expected); // passes correctly

// This should fail, because the promise resolved to `expected`
const notExpected = [something, else];
await expect(results).resolves.toStrictEqual(notExpected); // fails correctly

我认为,假设 Promise 是不透明的,我确实需要 await 来断言它的值。我相信这样做不会发生额外的调用或工作,我可以通过断言 expect(mockSomeOtherWork).toHaveBeenCalledTimes(1) 来证明这一点。由于此断言在我上述解决方法之前和之后的测试中均成功,因此一个调用必须在 foo 的正文中。

如果 Jest 有一组 toHaveResolvedWith 匹配器,那就太好了,但这对于我今天的目的来说已经足够了。

【讨论】:

  • 谢谢!工作完美。 Jest docs 没有关于此的任何信息:/