クリックイベントのテスト方法は以前書いた。
JestでReactのテストをする(2) クリックイベントのテスト
今回は、テキストボックスやテキストエリアのチェンジイベントのテスト方法について書く。
テストの対象となるコードを準備する
まず、サンプルとして以下のコンポーネントを作成した。
// input.js import React from 'react'; export default class InputField extends React.Component { constructor(props){ super(props); this.state = {remainCount:10, value:''}; }; reflectInput(length, value){ this.setState({remainCount:10-length, value}); }; render(){ return( <div> <input type="text" onChange={e=>{ this.reflectInput.bind(this)(e.target.value.length, e.target.value); }}></input><br /> <span>{this.state.remainCount}</span><br /> <button type="button" onClick={()=>{ if(this.state.remainCount === 10 || this.state.remainCount < 0){ return; }; this.props.clickFunc(this.state.value); }}>submit</button> </div> ); }; };
これをレンダリングすると、以下のようになる。
文字を入力すると、数字が減っていく。
10文字まで入力可能で、それ以上入力された状態だと、submitボタンを押しても何も起こらない。
また、何も入力されていない状態でも、submitボタンによる関数の呼び出しは起こらない。
以下のような仕組み。
- submitボタンで実行する関数を、
this.props.clickFunc
として外から渡す state
で、入力可能文字数と入力内容をremainCount
とvalue
として保持する- テキストボックスでチェンジイベントが発生する度、
setState()
でremainCount
とvalue
の値を更新する - submitボタンを押すとまず、
remainCount
の値をチェックして、問題があればその時点で処理を終了する - 問題がなければ、
value
を引数として関数を実行する
なお、ボタン押下時の文字数のチェックはstate
の値ではなく、その都度DOMから取得して行ったほうがいいと思うが、サンプルとしての分かりやすさを優先して、このような形にした。
テストを書く
この場合、以下の2つをチェックすればいいと思う。
onChange
を検知して正しくstate
を更新しているかどうかstate.remainCount
によるバリデーションが正しく機能しているか
以下がテストコード。
// __tests__/input.test.js import React from 'react'; import {shallow} from 'enzyme'; import InputField from '../input.js'; describe('inputのテスト', ()=>{ test('state', ()=>{ const subject = shallow(<InputField />); let remainCount = subject.state().remainCount; expect(remainCount).toBe(10); subject.find('input').simulate('change', {target: {value: 'abc'}}); remainCount = subject.state().remainCount; expect(remainCount).toBe(7); }); test('click', ()=>{ const mock = jest.fn(); const subject = shallow(<InputField clickFunc={mock} />); subject.find('button').simulate('click'); expect(mock).not.toHaveBeenCalled(); subject.find('input').simulate('change', {target: {value: 'abc'}}); subject.find('button').simulate('click'); expect(mock).toHaveBeenCalledTimes(1); subject.find('input').simulate('change', {target: {value: 'qwerty'}}); subject.find('button').simulate('click'); expect(mock).toHaveBeenCalledWith('qwerty'); expect(mock).toHaveBeenCalledTimes(2); subject.find('input').simulate('change', {target: {value: '01234567890'}}); let remainCount = subject.state().remainCount; expect(remainCount).toBe(-1); subject.find('button').simulate('click'); expect(mock).toHaveBeenCalledTimes(2); }); });
shallow
やtoHaveBeenCalled
については、冒頭のクリックイベントの記事を参照。
shallowの戻り値.find(対象となる要素).simulate(イベント名);
上記のように書くことで対象となる要素にイベントを起こせるのだが、これはchange
イベントも同様。
そして、simulate()
の第二引数に{target: {value: 任意の文字列}}
を渡すことで、対象の要素に任意の文字列をセットすることが出来る。
つまり以下は、subject
(shallow
でレンダリングしたもの)のinput
要素にabc
という文字列をセットしたのと同じ意味を持つ。
subject.find('input').simulate('change', {target: {value: 'abc'}});
テストの結果、state
によるバリデーションは正しく機能しており、関数の呼び出しにも問題がないことを確認できた。