不能用jest和测试函数调用来模拟模块

92dk7w1h  于 2023-11-15  发布在  Jest
关注(0)|答案(3)|浏览(190)

我使用create-app-component创建了一个项目,它使用构建脚本(babel,webpack,jest)配置了一个新的应用程序。
我写了一个React组件,我正在测试。该组件需要另一个JavaScript文件,公开一个函数。
我的search.js文件

export {
  search,
}

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

字符串
我的react组件:

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方法的参数,我需要在测试中使用模拟函数。
如果我转换mocking部分,使用变量:

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


我得到这个错误:
TypeError:(0,_search.search)不是函数
无论我尝试访问jest.fn并测试参数,我都无法使其工作。
我做错了什么?

jdg4fx2g

jdg4fx2g1#

问题

你得到这个错误的原因与各种操作是如何提升的有关。
即使在你的原始代码中,你只是在 * 为mockSearch赋值并调用jest的mock之后 * 才导入SearchContainer,规范指出:Before instantiating a module, all of the modules it requested must be available
因此,当SearchContainer被导入,然后又导入search时,mockSearch变量仍然是未定义的。
有人可能会觉得奇怪,因为这似乎也意味着search.js还没有被mocked,所以mocking根本不起作用。幸运的是,(babel-)jest确保将对mock和类似函数的调用提升到比导入更高的水平,所以mocking将起作用。
尽管如此,mock函数引用的mockSearch的赋值不会随着mock调用而提升。因此,相关操作的顺序如下:
1.为./search.js设置一个模拟工厂
1.导入所有依赖项,这将调用一个函数的mock工厂来给予组件
1.为mockSearch赋值
当第2步发生时,传递给组件的search函数将是未定义的,第3步的赋值已经太晚了,无法改变这一点。

解决方案

如果您将mock函数创建为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 } = jest.requireMock('./search.js');


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

附加说明

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

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

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

k7fdbhmy

k7fdbhmy2#

在我的例子中,我得到这个错误是因为我没有正确地实现mock。
我的失败代码:
第一个月
当它应该是一个箭头功能时...
jest.mock('react-native-some-module', () => mockedModule);

eqzww0vc

eqzww0vc3#

当用响应模拟一个API调用时,记得要使用bloc()=>你的测试并等待 Package 器更新。我的页面做了典型的componentDidMount => make API call => positive response set some state...然而 Package 器中的状态没有得到更新... bloc和await修复了这个问题...这个例子是为了简洁...

...otherImports etc...
const SomeApi = require.requireMock('../../api/someApi.js');

jest.mock('../../api/someApi.js', () => {
    return {
        GetSomeData: jest.fn()
    };
});

beforeEach(() => {
    // Clear any calls to the mocks.
    SomeApi.GetSomeData.mockClear();
});

it("renders valid token", async () => {
        const responseValue = {data:{ 
            tokenIsValid:true}};
        SomeApi.GetSomeData.mockResolvedValue(responseValue);
        let wrapper = shallow(<MyPage {...props} />);
        expect(wrapper).not.toBeNull();
        await wrapper.update();
        const state = wrapper.instance().state;
        expect(state.tokenIsValid).toBe(true);

    });

字符串

相关问题