Reactのコンポーネントには、属性として値を渡すことができ、渡された値にはthis.props.xxx
でアクセスできる。
props
の他に、自身で管理する値であるstate
というものもあるが、Reactの思想的にstate
はあまり使わないほうがいいのだろう。
コンポーネントはただ値を受け取り、その値に従って出力(レンダリング)を行うものであるべき。
props
には何でも渡すことが出来るが、propTypes
という機能を使って、渡された値をチェックすることが出来る。
詳しくは下記を参照。
React.jsのProp - Qiita
このpropTypes
をテストの対象とするのが、今回の目的。
例として、以下のようなPerson
コンポーネントを用意した。
import React from 'react'; export default class Person extends React.Component{ constructor(props){ super(props); }; render(){ const id = this.props.hasId ? '有' : '無' ; return( <div> <h1>{this.props.name}</h1> <p>年齢: {this.props.age}</p> <p>会員証: {id}</p> </div> ); }; }; Person.propTypes = { name: React.PropTypes.string.isRequired, age: React.PropTypes.number.isRequired, hasId: React.PropTypes.bool.isRequired };
name
、age
、hasId
という3つのprops
を受け取るが、全て必須であり、それぞれ文字列、数値、真偽値、でないといけない。
例えば<Person name="Tom" hasId={true} />
とレンダリングすると、以下の警告が出る。
Warning: Failed prop type: Required prop `age` was not specified in `Person`. in Person
ただ、あくまでもconsole.warn()
でメッセージが出るだけであり、エラーにはならない。
レンダリングも行われる。年齢が入るべき部分は、空白となる。
次に、<Person name="Tom" age={20} hasId={0} />
とレンダリングしてみる。真偽値であるべきhasId
が、数値になっている。
Warning: Failed prop type: Invalid prop `hasId` of type `number` supplied to `Person`, expected `boolean`. in Person
正しく、警告が出ている。ただやはりレンダリングはされるし、しかも、会員証の部分に「無」と表示されている。
これは、JavaScriptによって0
がfalse
に型変換されるからである。
このことからも、処理自体は通常通り行われることが分かる。
そのため、props
の値がおかしかったとしても、テストでは見逃されてしまう。
例えば、以下のようなテストを書いたとする。
import React from 'react'; import {shallow} from 'enzyme'; import Person from '../types.js'; describe('propTypesのテスト', ()=>{ test('正しいケース', ()=>{ const subject = shallow(<Person name="Tom" age={20} hasId={true} />); }); test('ageがない', ()=>{ const subject = shallow(<Person name="Tom" hasId={true} />); }); test('hasIdの型が違う', ()=>{ const subject = shallow(<Person name="Tom" age={20} hasId={0} />); }); });
コンポーネントに渡される値が正しくないが、テストはパスする。
console.warn()
でメッセージが表示されるので見落とすことはないかもしれないが、テスト自体を通らないようにしたほうが、テストとしては望ましいと思う。
そこで、以下の記述を追加することで、テストに引っかかるようになる。
console.error = error => { throw new Error(error); };
以下のテストを実行すると、テストは通らない。
import React from 'react'; import {shallow} from 'enzyme'; import Person from '../types.js'; describe('propTypesのテスト', ()=>{ console.error = error => { throw new Error(error); }; test('正しいケース', ()=>{ const subject = shallow(<Person name="Tom" age={20} hasId={true} />); }); test('ageがない', ()=>{ const subject = shallow(<Person name="Tom" hasId={true} />); }); test('hasIdの型が違う', ()=>{ const subject = shallow(<Person name="Tom" age={20} hasId={0} />); }); });
FAIL __tests__/types.test.js ● propTypesのテスト › ageがない Warning: Failed prop type: Required prop `age` was not specified in `Person`. in Person at CustomConsole.console.error (__tests__/types.test.js:7:9) at Object.<anonymous> (__tests__/types.test.js:13:107) at process._tickCallback (internal/process/next_tick.js:103:7) ● propTypesのテスト › hasIdの型が違う Warning: Failed prop type: Invalid prop `hasId` of type `number` supplied to `Person`, expected `boolean`. in Person at CustomConsole.console.error (__tests__/types.test.js:7:9) at Object.<anonymous> (__tests__/types.test.js:16:107) at process._tickCallback (internal/process/next_tick.js:103:7) propTypesのテスト ✓ 正しいケース (10ms) ✕ ageがない (3ms) ✕ hasIdの型が違う (1ms)
console.error
の記述は、上記のように先頭に書いても、あるいは中間や末尾に書いても、問題なく機能した。
先頭に書くのが、見やすい気はする。