Javascript Jest 预期模拟函数已被调用,但未被调用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/51418086/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 04:49:52  来源:igfitidea点击:

Jest Expected mock function to have been called, but it was not called

javascriptreactjsbabeljsjestjsenzyme

提问by styler

I've looked at various suggestions to solve testing a class property with no success and was wondering if anyone could possibly cast a little more light on where I may be going wrong, here are the tests I've tried all with the error Expected mock function to have been called, but it was not called.

我查看了解决测试类属性但没有成功的各种建议,并想知道是否有人可以更清楚地了解我可能出错的地方,以下是我尝试过的所有测试,错误为 Expected mock函数已被调用,但未调用。

Search.jsx

搜索.jsx

import React, { Component } from 'react'
import { func } from 'prop-types'
import Input from './Input'
import Button from './Button'

class SearchForm extends Component {
  static propTypes = {
    toggleAlert: func.isRequired
  }

  constructor() {
    super()

    this.state = {
      searchTerm: ''
    }

    this.handleSubmit = this.handleSubmit.bind(this)
  }

  handleSubmit = () => {
    const { searchTerm } = this.state
    const { toggleAlert } = this.props

    if (searchTerm === 'mocky') {
      toggleAlert({
        alertType: 'success',
        alertMessage: 'Success!!!'
      })

      this.setState({
        searchTerm: ''
      })
    } else {
      toggleAlert({
        alertType: 'error',
        alertMessage: 'Error!!!'
      })
    }
  }

  handleChange = ({ target: { value } }) => {
    this.setState({
      searchTerm: value
    })
  }

  render() {
    const { searchTerm } = this.state
    const btnDisabled = (searchTerm.length === 0) === true

    return (
      <div className="well search-form soft push--bottom">
        <ul className="form-fields list-inline">
          <li className="flush">
            <Input
              id="search"
              name="search"
              type="text"
              placeholder="Enter a search term..."
              className="text-input"
              value={searchTerm}
              onChange={this.handleChange}
            />
            <div className="feedback push-half--right" />
          </li>
          <li className="push-half--left">
            <Button className="btn btn--positive" disabled={btnDisabled} onClick={this.handleSubmit}>
              Search
            </Button>
          </li>
        </ul>
      </div>
    )
  }
}

export default SearchForm

First option:

第一个选项:

it('should call handleSubmit function on submit', () => {
    const wrapper = shallow(<Search toggleAlert={jest.fn()} />)
    const spy = jest.spyOn(wrapper.instance(), 'handleSubmit')
    wrapper.instance().forceUpdate()
    wrapper.find('.btn').simulate('click')
    expect(spy).toHaveBeenCalled()
    spy.mockClear()
  })

Second option:

第二种选择:

it('should call handleSubmit function on submit', () => {
    const wrapper = shallow(<Search toggleAlert={jest.fn()} />)
    wrapper.instance().handleSubmit = jest.fn()
    wrapper.update()
    wrapper.find('.btn').simulate('click')
    expect(wrapper.instance().handleSubmit).toHaveBeenCalled()
  })

I get that with a class property the function is an instance of the class requiring the component to be updated in order to register the function, it looks however like the component handleSubmit function gets called instead of the mock?

我得到一个类属性,该函数是类的一个实例,需要更新组件以注册该函数,但是它看起来像组件 handleSubmit 函数被调用而不是模拟?

Swapping out handleSubmit to be a class function as a method gives me access on the class prototype which passes the test when spying on Search.prototype but I'd really like to get a solution to the class property approach.

将 handleSubmit 交换为类函数作为方法使我可以访问在监视 Search.prototype 时通过测试的类原型,但我真的很想获得类属性方法的解决方案。

All suggestions and recommendations would be grateful!

所有建议和建议将不胜感激!

回答by Kuldeep Bhimte

I suppose the unit test should be robust enough to catch the error, if case of any undesirable code changes.

我想单元测试应该足够健壮,可以在error任何不受欢迎的代码更改的情况下捕捉到。

Please include strict assertions in your tests.

请在您的测试中包含严格的断言。

For the conditional statements, please cover the branches as well. E.g in case of ifand elsestatement you will have to write twotests.

对于条件语句,也请覆盖分支。例如,在ifandelse语句的情况下,您将不得不编写two测试。

For user actions, you should try to simulate the actions rather than calling the function manually.

对于用户操作,您应该尝试模拟操作而不是手动调用函数。

Please see the example below,

请看下面的例子,

import React from 'react';
import { shallow } from 'enzyme';
import { SearchForm } from 'components/Search';


describe('Search Component', () => {
  let wrapper;
  const toggleAlert = jest.fn();
  const handleChange = jest.fn();
  const successAlert = {
    alertType: 'success',
    alertMessage: 'Success!!!'
  }
  const errorAlert = {
    alertType: 'error',
    alertMessage: 'Error!!!'
  }
  beforeEach(() => {
    wrapper = shallow(<SearchForm toggleAlert={toggleAlert} />);
  });
  it('"handleSubmit" to have been called with "mocky"', () => {
    expect(toggleAlert).not.toHaveBeenCalled();
    expect(handleChange).not.toHaveBeenCalled();
    wrapper.find('Input').simulate('change', { target: { value: 'mocky' } });
    expect(handleChange).toHaveBeenCalledTimes(1);
    expect(wrapper.state().searchTerm).toBe('mocky');
    wrapper.find('Button').simulate('click');
    expect(toggleAlert).toHaveBeenCalledTimes(1);
    expect(toggleAlert).toHaveBeenCalledWith(successAlert);
    expect(wrapper.state().searchTerm).toBe('');
  });

  it('"handleSubmit" to have been called with "other than mocky"', () => {
    expect(toggleAlert).not.toHaveBeenCalled();
    expect(handleChange).not.toHaveBeenCalled();
    wrapper.find('Input').simulate('change', { target: { value: 'Hello' } });
    expect(handleChange).toHaveBeenCalledTimes(1);
    expect(wrapper.state().searchTerm).toBe('Hello');
    wrapper.find('Button').simulate('click');
    expect(toggleAlert).toHaveBeenCalledTimes(1);
    expect(toggleAlert).toHaveBeenCalledWith(errorAlert);
    expect(wrapper.state().searchTerm).toBe('Hello');
  });
});

回答by styler

So I've managed to create a working solution by first of all updating the wrapper instance and then updating the wrapper. Test now works.

因此,我首先更新包装器实例,然后更新包装器,从而设法创建了一个有效的解决方案。测试现在有效。

Working test looks like:

工作测试看起来像:

it('should call handleSubmit function on submit', () => {
    const wrapper = shallow(<Search toggleAlert={jest.fn()} />)
    wrapper.instance().handleSubmit = jest.fn()
    wrapper.instance().forceUpdate()
    wrapper.update()
    wrapper.find('.btn').simulate('click')
    expect(wrapper.instance().handleSubmit).toHaveBeenCalled()
  })

回答by trevorgk

You shouldn't need to write unit tests for this scenario. You should be able to trust that the framework will fire the correct handlers that you've provided. A more useful test would be one which mocks the toggleAlertprop and tests the instance method handleSubmit. This is where the majority of custom logic will reside and consequently where we are most likely to find errors. Snapshot testing should be fine for anything that is part of the render function output.

您不需要为此场景编写单元测试。您应该能够相信框架会触发您提供的正确处理程序。一个更有用的测试是模拟toggleAlertprop 并测试实例方法handleSubmit。这是大多数自定义逻辑所在的位置,因此我们最有可能发现错误的位置。快照测试应该适用于渲染函数输出的任何部分。

A sensible test suite for this component would resemble something like the following:

该组件的合理测试套件类似于以下内容:

describe('handleSubmit', () => {
  let wrapper;
  let spy;

  describe('when searchTerm is "mocky"', () => {
    beforeEach(() => {
      spy = jest.fn();
      wrapper = shallow(<SearchForm toggleAlert={spy} />);
      wrapper.setState({ searchTerm: 'mocky' });
    });

    it('will fire spy with expected arguments', () => {
      // verify that spy has not been fired prior to test
      expect(spy).not.toBeCalled();

      wrapper.instance().handleSubmit();

      expect(spy).toBeCalled();
      expect(spy).toBeCalledWith({
        alertType: 'success',
        alertMessage: 'Success!!!'
      });
    });

    it('will set searchTerm to ""', () => {
      expect(wrapper.state('searchTerm')).toBe('mocky');
      wrapper.instance().handleSubmit();
      expect(wrapper.state('searchTerm')).toBe('');
    });
  });

  describe('when searchTerm is "something else"', () => {
    beforeEach(() => {
      spy = jest.fn();
      wrapper = shallow(<SearchForm toggleAlert={spy} />);
      wrapper.setState({ searchTerm: 'something else' });
    });

    it('will fire spy with expected arguments', () => {
      // verify that spy has not been fired prior to test
      expect(spy).not.toBeCalled();

      wrapper.instance().handleSubmit();

      expect(spy).toBeCalled();
      expect(spy).toBeCalledWith({
        alertType: 'error',
        alertMessage: 'Error!!!'
      });
    });
  });
});

回答by Shubham Agarwal Bhewanewala

Try something like this

尝试这样的事情

it('should call handleSubmit function on submit', () => {
        const toggleAlert = jest.fn();
        const wrapper = shallow(<Search toggleAlert={toggleAlert} />)
        wrapper.setState({ searchText: 'mocky' });
        wrapper.find('Button').at(0).simulate('click');
        expect(toggleAlert).toHaveBeenLastCalledWith({
                   alertType: 'success',
                   alertMessage: 'Success!!!'
              });
      })

****Update

****更新

 constructor(props) {
    super(props) //you have to add props to access it this.props

    this.state = {
      searchTerm: ''
    }

    this.handleSubmit = this.handleSubmit.bind(this)
  }

回答by user13069482

A solution that could work is;

一个可行的解决方案是;

Mock the function before shallow:

在浅之前模拟函数:

let handleSubmitMock = jest.fn();
LoginPage.prototype.handleSubmit = function() {  handleSubmitMock() };

Use this to expect:

使用它来期待:

form.props.onSubmit();
expect(handleSubmitMock).toHaveBeenCalledTimes(1);