create-react-appでプロジェクトを作成してテストを実行する
create-react-appでtestingというプロジェクトを作成します。
create-react-app testing
最初のテスト
さっそく簡単なテストを実行してみます。
App.jsを以下のように編集します。
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
return <div className="App">こんにちわ</div>;
}
}
export default App;
「こんにちわ」とブラウザ画面上に表示するだけのアプリです。
このアプリが期待通りの動作をしているかテストします。
create-react-app でtestingプロジェクトを作成した時点でsrc内にApp.test.jsというファイルが作成されています。これにexpect(div.innerHTML).toContain('こんにちわ');
を追記します。
App.test.jsは以下になります。
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
expect(div.innerHTML).toContain('こんにちわ');
ReactDOM.unmountComponentAtNode(div);
});
it('renders without crashing', () => { ....
の it は英語のit(それ)です。
it renders without crashing (直訳すると「それはクラッシュすることなく描画しました」)
です。it(それ) はテストを意味してますね。
expect(div.innerHTML).toContain('こんにちわ');
は<App />
内のinnerHTMLに「こんにちわ」が含まれているかどうかをテストします。”toContain”は含まれているかどうかをチェックする関数です。
npm run test
と表示されれば成功です。
Tests : 1passed, 1 total
となっています。無事にテストがパスしています。
テストを終了する時はCtrl + Cを押します。
テストが実行される流れ
Enzymeライブラリをインストール&設定する
もっと複雑なテストを簡単に行うためにEnzymeというテストユーティリティライブラリを使用します。
EnzymeはAirbnb製のオープンソースライブラリです。
enzymeのドキュメントはこちら → https://airbnb.io/enzyme/
Enzymeをreactで使用する場合2つのライブラリをインストールする必要があります。reactのバージョンが16の場合、enzyme と enzyme-adapter-react-16をインストールします(reactのバージョンによって16の部分を15または17などに変更する必要があります)。
npm install enzyme enzyme-adapter-react-16
setupTests.jsというファイルをsrcフォルダ内に作成する
Jestはsrcフォルダ内のsetupTests.js
というファイルを探して設定を読み込みます。setupTests.js
という名前でファイルを作る必要があります。
setupTests.js
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });
Enzyme API
Static render
指定したコンポーネントを描画してそのHTMLを返します。
Shallow render
指定されたコンポーネントのみ描画して子コンポーネントは描画しない。
つまり、指定したコンポーネントの動作のみ確認できる。
Full DOM render
すべての描画を行います。
beforeEach → it → afterEach
Full DOM renderを行う時にはmountとunmountを使用します。
コンポーネントのマウントやアンマウントなどの操作は多くのitで共通して行うので、Jestの関数であるbeforeEachやafterEachを使って、共通する作業をitの外に出します。
let wrapped;
beforeEach(() => {
wrapped = mount(<CommentBox />);
});
afterEach(() => {
wrapped.unmount();
});
elementがあるかどうかテストする
mountとunmountをbeforeEachとafterEach内で行ったので、itはより簡潔に書くことができます。
it('has a text area and a button', () => {
expect(wrapped.find('textarea').length).toEqual(1);
expect(wrapped.find('button').length).toEqual(1);
});
文字入力ができるかテストする
- textarea elementがあるかどうかチェック
- changeイベントをシミュレート
- 仮のイベントを渡してみる
- コンポーネントをアップデートする
- textareaの値が変更されたかを確認する
changeイベントをenzymeのsimulateでシミュレート
enzyme の simulateメソッドを使う。
仮の値(mock)をsimulateの第2引数として渡すことができます。
React ではonChangeですが、simulate('change')としてシミュレートします。
wrapped.update()
でコンポーネントを確実にアップデートする。
その後、expectでexpect(wrapped.find('textarea').prop('value')).toEqual('new comment');
valueの値を読み込んで(.prop)渡した文字列'new comment'と同じであるか(.toEqual)比較しています。
.propはelementのプロパティ値を取得することができます。
import React from 'react';
import { mount } from 'enzyme';
import CommentBox from 'components/CommentBox';
let wrapped;
beforeEach(() => {
wrapped = mount(<CommentBox />);
});
afterEach(() => {
wrapped.unmount();
});
it('has a text area and a button', () => {
expect(wrapped.find('textarea').length).toEqual(1);
expect(wrapped.find('button').length).toEqual(1);
});
it('has a text area that users can type in', () => {
wrapped
.find('textarea')
.simulate('change', { target: { value: 'new comment' } });
wrapped.update();
expect(wrapped.find('textarea').prop('value')).toEqual('new comment');
});
it('when form is submitted, text area gets emptied', () => {
wrapped.find('textarea').simulate('change', {
target: { value: 'new comment' }
});
wrapped.update();
wrapped.find('form').simulate('submit');
wrapped.update();
expect(wrapped.find('textarea').prop('value')).toEqual('');
});
重複する動作を外に出す
wrapped
.find('textarea')
.simulate('change', { target: { value: 'new comment' } });
wrapped.update();
は重複して2つのit(...)
でてくるので、
describe('テキストエリア', () => {
beforeEach(() => {
wrapped
.find('textarea')
.simulate('change', { target: { value: 'new comment' } });
wrapped.update();
});
it('has a text area that users can type in', () => {
expect(wrapped.find('textarea').prop('value')).toEqual('new comment');
});
it('when form is submitted, text area gets emptied', () => {
wrapped.find('form').simulate('submit');
wrapped.update();
expect(wrapped.find('textarea').prop('value')).toEqual('');
});
});
のように、共通する操作を持つit
をdescribe
で切り出してきてその中でbeforeEach
を適用します。
Reduxを使ったステート管理をテストするには
コメント