Reactアプリをテスト
Facebook ではJestを使用して、Reactアプリケーションをテストします。
セットアップ
Create React Appを使用したセットアップ
Reactに馴染みがないなら、Create React Appの利用をお勧めします。 すぐに使えて Jestも同梱されています! スナップショットをレンダリングするには、 react-test-renderer
を追加するだけです。
実行
- npm
- Yarn
- pnpm
npm install --save-dev react-test-renderer
yarn add --dev react-test-renderer
pnpm add --save-dev react-test-renderer
Create React Appを使わないセットアップ
既存のアプリケーションがある場合は、いくつかのパッケージをインストールしてうまく機能するようにする必要があります。 babel-jest
パッケージと react
のbabel presetをテスト環境内のコードを変換するのに利用しています。 using babelも参照して下さい。
実行
- npm
- Yarn
- pnpm
npm install --save-dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
yarn add --dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
pnpm add --save-dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
package.json
は以下のようなものになっているはずです( <current-version>
はパッケージの実際の最新版のバージョンの数字になります)。 scriptsとJestの設定のエントリを追加して下さい:
{
"dependencies": {
"react": "<current-version>",
"react-dom": "<current-version>"
},
"devDependencies": {
"@babel/preset-env": "<current-version>",
"@babel/preset-react": "<current-version>",
"babel-jest": "<current-version>",
"jest": "<current-version>",
"react-test-renderer": "<current-version>"
},
"scripts": {
"test": "jest"
}
}
module.exports = {
presets: [
'@babel/preset-env',
['@babel/preset-react', {runtime: 'automatic'}],
],
};
それでは次へ進みましょう!
スナップショットテスト
ハイパーリンクをレンダリングするLinkコンポーネントの snapshot test を作成しましょう:
import {useState} from 'react';
const STATUS = {
HOVERED: 'hovered',
NORMAL: 'normal',
};
export default function Link({page, children}) {
const [status, setStatus] = useState(STATUS.NORMAL);
const onMouseEnter = () => {
setStatus(STATUS.HOVERED);
};
const onMouseLeave = () => {
setStatus(STATUS.NORMAL);
};
return (
<a
className={status}
href={page || '#'}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
{children}
</a>
);
}
例として関数コンポーネントを使用していますが、クラスコンポーネントも同じ方法でテストすることができます。 React: 関数コンポーネントとクラスコンポーネントを参照してください。 リマインド: クラスコンポーネントでは、メソッドを直接テストするのではなく、propsをテストするためにJestを使用することを想定しています。
コンポーネントとのやり取りとレンダリングされた出力をキャプチャしてスナップショットファイルを作成するために、ReactのテストレンダラーとJestのスナップショット機能を利用しましょう:
import renderer from 'react-test-renderer';
import Link from '../Link';
it('changes the class when hovered', () => {
const component = renderer.create(
<Link page="http://www.facebook.com">Facebook</Link>,
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
renderer.act(() => {
tree.props.onMouseEnter();
});
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
renderer.act(() => {
tree.props.onMouseLeave();
});
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
yarn test
または jest
を実行すると、このようなファイルが出力されます:
exports[`changes the class when hovered 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Facebook
</a>
`;
exports[`changes the class when hovered 2`] = `
<a
className="hovered"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Facebook
</a>
`;
exports[`changes the class when hovered 3`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Facebook
</a>
`;
次回のテストでは、レンダリングされた出力は前に作成されたスナップショットと比較されます。 スナップショットは、コードの変更に沿ってコミットさ れるべきです。 スナップショットテストが失敗した場合、それが意図的な変更かどうかを点検する必要があります。 変更が予想されたものであればjest -u
コマンドでJestを実行して既存のスナップショットを上書きします。
The code for this example is available at examples/snapshot.
モック、Enzyme、 React 16+ を使用したスナップショットテスト
Enzyme と React 16 以降を使用している場合、スナップショットテストには注意点があります。 以下のスタイルを使用しているモジュールをモックアウトする場合:
jest.mock('../SomeDirectory/SomeComponent', () => 'SomeComponent');
コンソールに次のような警告が表示されます。
Warning: <SomeComponent /> is using uppercase HTML. Always use lowercase HTML tags in React.
# または
Warning: The tag <SomeComponent> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.
React 16 がこれらの警告を引き起こしてしまう理由は、要素の型チェックの方法のためであり、モック化したモジュールはこのチェックに引っかかってしまいます。 これに対処するための選択肢は以下のとおりです。
- テキストとしてレンダリングする。 この方法を選んだ場合、スナップショット内のモックコンポーネントに渡された props を確認することができませんが、シンプルで分かりやすい方法です。
js
jest.mock('./SomeComponent', () => () => 'SomeComponent');