Jest Expected模拟函数已被调用,但未被调用

时间:2018-07-19 08:41:42

标签: javascript reactjs babeljs jestjs enzyme

我查看了各种建议,以解决无法成功测试类属性的问题,并且想知道是否有人可以对我可能会出错的地方多加一些启示,这是我尝试过的所有测试错误预期已调用模拟函数,但未调用。

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

第一个选项:

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

我发现具有class属性的函数是该类的实例,需要对组件进行更新才能注册该函数,但是看起来好像要调用组件handleSubmit函数而不是模拟吗?

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

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

5 个答案:

答案 0 :(得分:5)

我认为您的单元测试应该足够健壮,以防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');
  });
});

答案 1 :(得分:1)

因此,通过首先更新包装实例,然后更新包装,我设法创建了一个可行的解决方案。现在测试可以正常工作。

工作测试如下:

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

答案 2 :(得分:1)

一种可行的解决方案是;

先浅析浅函数:

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

使用此期望值:

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

答案 3 :(得分:0)

尝试类似的方法

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

答案 4 :(得分:0)

对于这种情况,您不需要编写单元测试。您应该能够相信该框架将触发您提供的正确处理程序。一种更有用的测试是模拟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!!!'
      });
    });
  });
});