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

匿名 (未验证) 提交于 2019-12-03 01:38:01

问题:

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.

Search.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?

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.

All suggestions and recommendations would be grateful!

回答1:

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

Please include strict assertions in your tests.

For the conditional statements, please cover the branches as well. E.g in case of if and else statement you will have to write two tests.

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');   }); });


回答2:

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()   }) 


回答3:

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)   } 


回答4:

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 toggleAlert prop 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.

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!!!'       });     });   }); }); 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!