如何模拟具有提取调用的函数调用

时间:2019-09-25 06:22:53

标签: reactjs jestjs enzyme

我的反应代码如下

handleSubmit(event) {
  event.preventDefault();

    this.setState({ isDone: true });

    ...do some operations...

    this.getName();
}

上面的handleSubmitGetNames组件的一部分,该组件依次调用下面的另一个函数,再次是同一组件的一部分。

getName() {
   var urlendPoint = new URL(propertiesInFile.urltoPing)

    var params = {
      firstName: this.state.firstName,
      lastName: this.state.lastName
    }

    urlendPoint.search = new URLSearchParams(params)

    fetch(urlendPoint).then(response => response.json()).then((data) => {
        // ...perform some actions...
    })
      .catch(error => {
        alert("Please check the names and try again");
      });
}

我正在为上述功能编写测试用例,但没有获得如何模拟获取数据的方法。以下是我的测试用例

describe('GetNames submit', () => {
    let wrapper = shallow(<GetNames />).instance();
    let getDetailsSpy;

    beforeEach(() => {
        getDetailsSpy = jest.spyOn(GetDetails.prototype, 'getDetails');
        wrapper = shallow(<GetDetails></GetDetails>);
    });

    afterEach(() => {
        jest.resetAllMocks();
    });

    it('check submit', () => {

      expect(wrapper.find('form')).toHaveLength(1);
      const formEventMocked = { preventDefault: jest.fn() };
      const state = {
        firsName: 'test1',
        lastName: 'test2',
        isDone: false
      };
      wrapper.setState(state);
      wrapper.find('form').simulate('submit', formEventMocked);

     fetch.mockResponse(() => getName().then(res => ({body: res})));

      expect(getDetailsSpy).toBeCalledTimes(1);
      expect(formEventMocked.preventDefault).toBeCalledTimes(1);
      expect(wrapper.state('loading')).toBeTruthy();
    });
});

在上面,我模拟了getDetails的响应,但是在fetch调用中测试失败,并显示错误ReferenceError: fetch is not defined

如何创建此测试用例,以返回fetch调用结果或调用该方法,并使用模拟对象对所有fetch进行测试?

1 个答案:

答案 0 :(得分:0)

您可以使用wrapper.instance().someMethod测试getName的{​​{1}}方法。

我在nodejs环境中使用Component模块,在浏览器环境中也是如此。您可以手动使用node-fetch方法模拟jest.mocknode-fetch模块。

window.fetch

index.tsx

import React, { Component } from 'react'; import fetch from 'node-fetch'; import console = require('console'); export interface ISomeComponentState { firstName: string; lastName: string; isDone: boolean; [key: string]: any; } export class SomeComponent extends Component<any, ISomeComponentState> { constructor(props) { super(props); this.state = { firstName: '', lastName: '', isDone: false }; this.handleSubmit = this.handleSubmit.bind(this); this.handleInputChange = this.handleInputChange.bind(this); } public render() { return ( <div> <form onSubmit={this.handleSubmit}> <label> firstName: <input type="text" name="firstName" placeholder="enter your first name" value={this.state.firstName} onChange={this.handleInputChange} /> </label> <label> lastName: <input type="text" name="lastName" placeholder="enter your last name" value={this.state.lastName} onChange={this.handleInputChange} /> </label> <div> <input type="submit" value="Submit" /> </div> </form> </div> ); } private handleInputChange(event: React.ChangeEvent<HTMLInputElement>) { const target = event.target; const name = target.name; const value = target.value; this.setState({ [name]: value }); } private handleSubmit(event: React.FormEvent<HTMLFormElement>) { event.preventDefault(); this.setState({ isDone: true }); const nameMatch = () => { return !(this.state.firstName.match('^Cel.*') && this.state.firstName.endsWith('on')); }; if (nameMatch()) { alert('Please enter a valid name'); return; } if (!this.state.lastName.endsWith('on')) { alert('check lastname too'); return; } this.getFullName(); } private getFullName() { // const urlendPoint = new URL('https://github.com/mrdulin'); const params = { firstName: this.state.firstName, lastName: this.state.lastName }; const urlendPoint = `https://github.com/mrdulin?fistName=${params.firstName}&lastName=${params.lastName}`; // (urlendPoint as any).search = new URLSearchParams(params); return fetch(urlendPoint) .then(response => response.json()) .then(data => data) .catch(error => { alert('Please check the names and try again'); }); } }

index.spec.tsx

带有覆盖率报告的单元测试结果:

import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { SomeComponent, ISomeComponentState } from './';
import console = require('console');
import fetch from 'node-fetch';

const { Response } = jest.requireActual('node-fetch');

jest.mock('node-fetch', () => jest.fn());

describe('SomeComponent', () => {
  describe('#handleSubmit', () => {
    let wrapper: ShallowWrapper;
    let getFullNameSpy;
    let alertSpy;
    beforeEach(() => {
      alertSpy = jest.spyOn(window, 'alert');
      getFullNameSpy = jest.spyOn(SomeComponent.prototype as any, 'getFullName');
      wrapper = shallow(<SomeComponent></SomeComponent>);
    });
    afterEach(() => {
      jest.resetAllMocks();
      getFullNameSpy.mockRestore();
    });
    it('check submit', () => {
      expect(wrapper.find('form')).toHaveLength(1);
      getFullNameSpy.mockResolvedValueOnce('fullName');
      const formEventMocked = { preventDefault: jest.fn() };
      const state: ISomeComponentState = {
        firstName: 'Cel.du.on',
        lastName: 'lin.on',
        isDone: false
      };
      wrapper.setState(state);
      expect(wrapper).toMatchSnapshot();
      wrapper.find('form').simulate('submit', formEventMocked);
      expect(getFullNameSpy).toBeCalledTimes(1);
      expect(formEventMocked.preventDefault).toBeCalledTimes(1);
      expect(wrapper.state('isDone')).toBeTruthy();
    });

    it('should alert when first name is invalid', () => {
      expect(wrapper.find('form')).toHaveLength(1);
      const formEventMocked = { preventDefault: jest.fn() };
      const state: ISomeComponentState = {
        firstName: 'du',
        lastName: 'lin.on',
        isDone: false
      };
      wrapper.setState(state);
      expect(wrapper).toMatchSnapshot();
      wrapper.find('form').simulate('submit', formEventMocked);
      expect(alertSpy).toBeCalledWith('Please enter a valid name');
      expect(formEventMocked.preventDefault).toBeCalledTimes(1);
      expect(wrapper.state('isDone')).toBeTruthy();
    });

    it('should alert when last name is invalid', () => {
      expect(wrapper.find('form')).toHaveLength(1);
      const formEventMocked = { preventDefault: jest.fn() };
      const state: ISomeComponentState = {
        firstName: 'Cel.du.on',
        lastName: 'lin',
        isDone: false
      };
      wrapper.setState(state);
      expect(wrapper).toMatchSnapshot();
      wrapper.find('form').simulate('submit', formEventMocked);
      expect(alertSpy).toBeCalledWith('check lastname too');
      expect(formEventMocked.preventDefault).toBeCalledTimes(1);
      expect(wrapper.state('isDone')).toBeTruthy();
    });
  });

  describe('#getFullName', () => {
    let wrapper: ShallowWrapper;
    let alertSpy;
    beforeEach(() => {
      alertSpy = jest.spyOn(window, 'alert');
      wrapper = shallow(<SomeComponent></SomeComponent>);
    });
    it('should fetch data correctly', async () => {
      (fetch as jest.MockedFunction<typeof fetch>).mockResolvedValueOnce(
        new Response(JSON.stringify({ data: 'mocked data' }))
      );
      wrapper.setState({ firstName: 'du', lastName: 'lin' });
      const actualValue = await (wrapper.instance() as any).getFullName();
      expect(actualValue).toEqual({ data: 'mocked data' });
      expect(fetch).toBeCalledWith(`https://github.com/mrdulin?firstName=du&lastName=lin`);
    });

    it('should alert when fetch data error', async () => {
      const mockedFetchError = new Error('some error');
      (fetch as jest.MockedFunction<typeof fetch>).mockRejectedValueOnce(mockedFetchError);
      wrapper.setState({ firstName: 'lin', lastName: 'du' });

      const actualValue = await (wrapper.instance() as any).getFullName();
      expect(actualValue).toBeUndefined();
      expect(fetch).toBeCalledWith(`https://github.com/mrdulin?firstName=lin&lastName=du`);
      expect(alertSpy).toBeCalledWith('Please check the names and try again');
    });
  });
});

以下是完整的演示:https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58085900