【问题标题】:Jest Expected mock function to have been called, but it was not called开玩笑 预期模拟函数已被调用,但未调用
【发布时间】:2018-12-27 07:44:06
【问题描述】:

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

搜索.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

第一个选项:

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

第二个选项:

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

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

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

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

【问题讨论】:

    标签: javascript reactjs babeljs jestjs enzyme


    【解决方案1】:

    我认为单元测试应该足够健壮以捕获error,如果发生任何不希望的代码更改。

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

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

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

    请看下面的例子,

    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】:

      所以我首先通过更新包装器实例然后更新包装器来创建一个有效的解决方案。现在可以测试了。

      工作测试如下:

      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】:

        试试这样的

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

        ****更新

         constructor(props) {
            super(props) //you have to add props to access it this.props
        
            this.state = {
              searchTerm: ''
            }
        
            this.handleSubmit = this.handleSubmit.bind(this)
          }
        

        【讨论】:

        • 嘿,所以我想针对被调用的 handleSubmit 函数进行测试,并想了解为什么我不能将 handleSubmit 用作类属性
        • 你想直接测试一个函数吗?
        • 测试应该在提交表单按钮时调用 handleSubmit 函数,所以 handleSubmit 是 Search 类的一个实例,即使我更新了包装器,我也无法在测试中调用它
        • 你可以将你的间谍传递给直接连接到道具的元素,所以我已经在我的方法中完成了。我通过了我的间谍并检查了是否正确调用了间谍。它甚至检查了句柄函数是否也被正确调用了
        【解决方案4】:

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

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

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

        【讨论】:

          【解决方案5】:

          一个可行的解决方案是;

          模拟浅层之前的函数:

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

          用这个来期待:

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

          【讨论】:

            猜你喜欢
            • 2019-10-27
            • 1970-01-01
            • 2019-03-23
            • 2019-03-28
            • 1970-01-01
            • 2019-11-19
            • 2019-07-03
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多