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

新手上路,请多包涵

我使用 create-app-component 创建了一个项目,它使用构建脚本(babel、webpack、jest)配置了一个新的应用程序。

我写了一个我正在尝试测试的 React 组件。该组件需要另一个 javascript 文件,公开一个函数。

我的 search.js 文件

export {
  search,
}

function search(){
  // does things
  return Promise.resolve('foo')
}

我的反应组件:

 import React from 'react'
import { search } from './search.js'
import SearchResults from './SearchResults'

export default SearchContainer {
  constructor(){
    this.state = {
      query: "hello world"
    }
  }

  componentDidMount(){
    search(this.state.query)
      .then(result => { this.setState({ result, })})
  }

  render() {
    return <SearchResults
            result={this.state.result}
            />
  }
}

在我的单元测试中,我想检查是否使用正确的参数调用了方法 search

我的测试看起来像这样:

 import React from 'react';
import { shallow } from 'enzyme';
import should from 'should/as-function';

import SearchResults from './SearchResults';

let mockPromise;

jest.mock('./search.js', () => {
  return { search: jest.fn(() => mockPromise)};
});

import SearchContainer from './SearchContainer';

describe('<SearchContainer />', () => {
  it('should call the search module', () => {
    const result = { foo: 'bar' }
    mockPromise = Promise.resolve(result);
    const wrapper = shallow(<SearchContainer />);

    wrapper.instance().componentDidMount();

    mockPromise.then(() => {
      const searchResults = wrapper.find(SearchResults).first();
      should(searchResults.prop('result')).equal(result);
    })
  })
});

我已经很难弄清楚如何使 jest.mock 工作,因为它需要变量以 mock 作为前缀。

但是如果我想测试方法 search 的参数,我需要在我的测试中提供模拟函数。

如果我转换模拟部分,使用变量:

 const mockSearch = jest.fn(() => mockPromise)
jest.mock('./search.js', () => {
  return { search: mockSearch};
});

我收到此错误:

TypeError: (0 , _search.search) 不是一个函数

无论我尝试访问 jest.fn 并测试参数,我都无法使其工作。

我究竟做错了什么?

原文由 ghusse 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 536
2 个回答

问题

您收到该错误的原因与提升各种操作的方式有关。

Even though in your original code you only import SearchContainer after assigning a value to mockSearch and calling jest’s mock , the specs point out that: Before instantiating a module, all of the modules it requested must be available

因此,在 SearchContainer 被导入时,又依次导入 search ,你的 mockSearch 变量仍然未定义。

人们可能会觉得这很奇怪,因为它似乎也暗示 search.js 尚未被模拟,因此模拟根本不起作用。幸运的是,(babel-)jest 确保 mock 的调用提升到比导入 更高 的类似函数,这样 mocking 就可以工作了。

尽管如此,模拟函数引用的 mockSearch 的分配将不会随 mock 调用一起提升。因此,相关操作的顺序将类似于:

  1. ./search.js
  2. 导入所有依赖项,这将调用模拟工厂的函数来提供组件
  3. 赋值给 mockSearch

当第 2 步发生时,传递给组件的 search 函数将是未定义的,第 3 步的赋值来不及改变它。

解决方案

如果您将模拟函数创建为 mock 调用的一部分(这样它也会被提升),当它被组件模块导入时,它将具有一个有效值,如您的早期示例所示。

正如您所指出的,当您想让模拟函数在您的测试中可用时,问题就开始了。对此有一个明显的解决方案:单独导入您已经模拟过的模块。

由于您现在知道 jest mocking 实际上发生在导入之前,所以一个简单的方法是:

 import { search } from './search.js'; // This will actually be the mock

jest.mock('./search.js', () => {
  return { search: jest.fn(() => mockPromise) };
});

[...]

beforeEach(() => {
  search.mockClear();
});

it('should call the search module', () => {
  [...]

  expect(search.mock.calls.length).toBe(1);
  expect(search.mock.calls[0]).toEqual(expectedArgs);
});

事实上,您可能想要替换:

 import { search } from './search.js';

和:

 const { search } = require.requireMock('./search.js');

这不应该产生任何功能上的差异,但可能会使您正在做的事情更加明确(并且应该帮助任何使用类型检查系统(例如 Flow)的人,因此它不会认为您正在尝试调用模拟函数在原来的 search )。

补充说明

如果您需要模拟的是模块本身的默认导出,那么所有这些都是绝对必要的。否则(正如@publicJorn 指出的那样),您可以简单地重新分配测试中的特定相关成员,如下所示:

 import * as search from './search.js';

beforeEach(() => {
  search.search = jest.fn(() => mockPromise);
});

原文由 Tomty 发布,翻译遵循 CC BY-SA 4.0 许可协议

就我而言,我收到此错误是因为我未能正确实施模拟。

我失败的代码:

jest.mock('react-native-some-module', mockedModule);

当它应该是一个箭头函数时……

jest.mock('react-native-some-module', () => mockedModule);

原文由 Nabil Freeman 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题