在开玩笑的模拟模块工厂中模拟一个承诺

Posted

技术标签:

【中文标题】在开玩笑的模拟模块工厂中模拟一个承诺【英文标题】:mocking a promise in jest mock module factory 【发布时间】:2020-01-31 05:15:59 【问题描述】:

我正在编写一些单元测试,需要从文件模拟命名导出,但当导出是返回承诺的函数时,它似乎不起作用。

例如,我有一个位于 lib/api.js 的模块,它导出一个名为 getFoo 的函数,该函数本身返回 axios.get('/foo')。

在我的测试中,我想将getFoo 的返回值模拟为带有 foo 有效负载的已解析承诺,因此我使用模块工厂进行模拟:

import * as api from 'lib/api';
jest.mock('lib/api', () => (
  getFoo: jest.fn(() => Promise.resolve('foo')),
);

然后在我的测试中,我想断言 getFoo 是作为更大的链式 Promise 过程的一部分调用的。

it('getBar should call getFoo', () => 
  return getBar().then(() => 
    expect(api.getFoo).toHaveBeenCalled();
  );
)

我在这里得到的错误是cannot read property 'then' of undefined,好像在模拟模块工厂中使用的 jest 函数没有返回承诺。

如果getFoo 的返回值不是一个承诺,它会按预期工作。如果我使用jest.fn() 作为getFoo 的值,然后在测试本身模拟实现...

it('getBar should call getFoo', () => 
  api.getFoo.mockImplementation(() => Promise.resolve("foo"));
  return getBar().then(() => 
    expect(api.getFoo).toHaveBeenCalled();
  );
)

...然后就可以了。我的理解是 jest.fn(() => ) 应该只是定义实现的一种更短的方式,所以我不确定为什么会出现错误。任何帮助表示赞赏。

谢谢。

更多信息:

lib/api 看起来像这样...

import axios from 'axios';

export const getFoo = (host: string, accessToken: string): Promise<> =>
  axios.get(`$host/foo`, 
    headers: 
      Authorization: `Bearer $accessToken`,
      'Content-Type': 'application/json',
    
  )
  // return foo
  .then((response) => response.data)
  .catch(() => 
    throw new Error('Failed to load foo');
  );

当我在测试运行时控制台记录模拟的 getFoo 函数时,它显示为...

getFoo function mockConstructor() 
  return fn.apply(this, arguments);                                                                         

【问题讨论】:

【参考方案1】:

首先,您需要在导入之前对其进行模拟:

jest.mock('lib/api');

import * as api from 'lib/api';

【讨论】:

感谢您的回复,我尝试在之后移动导入,但似乎并没有改变结果。我什至没有在测试文件本身中使用lib/api,所以也许我根本不需要导入它,但不管我得到同样的错误could not read property then of undefined 您可以在lib/api 中添加您要导出的内容吗?看起来您可能没有导出您尝试访问的内容。 我刚刚注意到您的实现中有一个错字,但这很奇怪,因为它应该返回一个错误:getFoo: jest.fn(() =&gt; Promise.resolve('foo'))。您缺少右括号。 对不起,我修好了,上面的代码我只是手动输入SO而不是从项目中复制+粘贴 让我们continue this discussion in chat。

以上是关于在开玩笑的模拟模块工厂中模拟一个承诺的主要内容,如果未能解决你的问题,请参考以下文章

如何开玩笑地模拟 i18next 模块

不能用玩笑模拟模块,并测试函数调用

开玩笑 - 用“模拟”前缀模拟不起作用

开玩笑:模拟 console.error - 测试失败

用 Jest 模拟基于承诺的请求

玩笑:使用默认和命名导出模拟 ES6 模块