web-dev-qa-db-ja.com

Jestモック関数が呼び出されたはずですが、呼び出されていません

クラスプロパティのテストを解決するためのさまざまな提案を見て成功していませんでしたが、間違っている可能性がある場所に誰かがもう少し光を当てることができるかどうか疑問に思っていました。関数が呼び出されましたが、呼び出されませんでした。

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

2番目のオプション:

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をスパイするときにテストに合格するクラスプロトタイプにアクセスできますが、クラスプロパティアプローチのソリューションを取得したいのです。

すべての提案と推奨事項に感謝します!

6
styler

望ましくないコードのケースが変更された場合、ユニットテストはerrorをキャッチするのに十分な堅牢性を備えている必要があります。

テストに厳格なアサーションを含めてください。

条件ステートメントについては、ブランチもカバーしてください。たとえば、ifおよびelseステートメントの場合、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');
  });
});
8
Kuldeep Bhimte

そこで、まずラッパーインスタンスを更新し、次にラッパーを更新することで、実用的なソリューションを作成することができました。テストが機能するようになりました。

動作テストは次のようになります。

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
styler

このようなものを試してください

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

このシナリオでは、単体テストを作成する必要はありません。フレームワークが提供した正しいハンドラーを起動することを信頼できるはずです。より有用なテストは、toggleAlert propをモックし、インスタンスメソッド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!!!'
      });
    });
  });
});
1
trevorgk