Testing React Native Apps
Facebookでは、Jestを使用して React Native アプリケーションをテストします。
動く React Native アプリケーションのテストに関するより深い知見は以下のシリーズを読むことで得てください: Part 1: Jest – Snapshot come into play、Part 2: Jest – Redux Snapshots for your Actions and Reducers
セットアップ
react-native 0.38 より react-native init
コマンドに Jest セットアップがデフォルトで含まれています。 以下の設定が自動的に package.json ファイルに追加されます。
{
"scripts": {
"test": "jest"
},
"jest": {
"preset": "react-native"
}
}
Note: もしあなたが react-native アプリケーションをアップグレードする際に jest-react-native
プリセットを利用していた場合はその依存を package.json
から削除し、 プリセットを react-native
に変更してください。
yarn test
を実行するだけで、Jest でテストを実行できます。
スナップショットテスト
さあ スナップショットテスト をいくつかのビューとテキストコンポーネントをもつサンプルコンポーネントに対して作りましょう。
// Intro.js
import React, {Component} from 'react';
import {StyleSheet, Text, View} from 'react-native';
class Intro extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>
This is a React Native snapshot test.
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
alignItems: 'center',
backgroundColor: '#F5FCFF',
flex: 1,
justifyContent: 'center',
},
instructions: {
color: '#333333',
marginBottom: 5,
textAlign: 'center',
},
welcome: {
fontSize: 20,
margin: 10,
textAlign: 'center',
},
});
export default Intro;
コンポーネントとのやり取りとレンダリングされた出力をキャプチャしてスナップショットファイルを作成するために、ReactのテストレンダラーとJestのスナップショット機能を利用しましょう:
// __tests__/Intro-test.js
import React from 'react';
import renderer from 'react-test-renderer';
import Intro from '../Intro';
test('renders correctly', () => {
const tree = renderer.create(<Intro />).toJSON();
expect(tree).toMatchSnapshot();
});
yarn test
または jest
を実行すると、このようなファイルが出力されます:
// __tests__/__snapshots__/Intro-test.js.snap
exports[`Intro renders correctly 1`] = `
<View
style={
Object {
"alignItems": "center",
"backgroundColor": "#F5FCFF",
"flex": 1,
"justifyContent": "center",
}
}>
<Text
style={
Object {
"fontSize": 20,
"margin": 10,
"textAlign": "center",
}
}>
Welcome to React Native!
</Text>
<Text
style={
Object {
"color": "#333333",
"marginBottom": 5,
"textAlign": "center",
}
}>
This is a React Native snapshot test.
</Text>
</View>
`;
次回のテストでは、レンダリングされた出力は前に作成されたスナップショットと比較されます。 The snapshot should be committed along with code changes. スナップショットテストが失敗した場合、それが意図的な変更かどうかを点検する必要があります。 変更が予想されたものであればjest -u
コマンドでJestを実行して既存のスナップショットを上書きします。
この例のコードは examples/react-native で参照できます。
プリセット設定
プリセットは環境をセットアップしますが、それはとても偏っており Facebook において有用であるとわかったものをベースにしています。 ただ、すべての設定値は上書き可能でプリセットを使ってない場合でもそれに合わせて設定することができます。
環境
react-native
は Jest プリセットとともに提供されているので、 package.json
の jest.preset
フィールドにはreact-native
が指定されているべきです。 プリセットは node 環境であり React Native アプリケーション環境を模倣します。そのため一切の DOM や ブラウザ API をロードしないため、Jest の起動時間を大きく向上させます。
transformIgnorePatterns のカスタム
The transformIgnorePatterns
option can be used to specify which files shall be transformed by Babel. 残念なことに多くの react-native npm モジュールは公開前にソースコードをコンパイルしていません。
デフォルトでは jest-react-native プリセットは自身のプロジェクトのソースファイルと react-native のみ処理します。 If you have npm dependencies that have to be transformed you can customize this configuration option by including modules other than react-native:
{
"transformIgnorePatterns": [
"node_modules/(?!(react-native|my-project|react-native-button)/)"
]
}
setupFiles
もしあなたが各テストファイルに対して追加設定を行いたい場合は、setupFiles
設定値 を使ってセットアップスクリプトを指定することが利用できます。
moduleNameMapper
moduleNameMapper
を使うとあるモジュールのパスを別のモジュールにマップすることができます。 デフォルトではプリセットはすべてのイメージをイメージスタブモジュールにマップしていますがもしモジュールが見つからない場合はこの設定値が役立つかもしれません。
{
"moduleNameMapper": {
"my-module.js": "<rootDir>/path/to/my-module.js"
}
}
Tips
jest.mock を使ってネイティブモジュールをモックする
react-native
に組み込まれている Jest プリセットはいくつかのデフォルトモックを持っており、それらは react-native レポジトリにも適用されています。 However, some react-native components or third party components rely on native code to be rendered. そのような場合には、Jest のマニュアルモックシステムを使って内部の実装部分をモックすることで問題を回避できる可能性があります。
例えば、もしあなたのコードが react-native-video
という名前のビデオコンポーネントに依存していた場合、あなたはそれをモック関数で置き換えたくなるかもしれません。
jest.mock('react-native-video', () => 'Video');
これはコンポーネントをそのコンポーネントのすべての props とともに <Video {...props} />
としてスナップショット出力の中で描画します。 caveats around Enzyme and React 16 も参照してください。
ときにより複雑なマニュアルモックを必要とする場合もあるでしょう。 例えば、もしあなたがネイティブコンポーネントの prop types もしくは静的フィールドをモックに渡したい場合に、jest-react-native のヘルパーを使って別の React モックコンポーネントを返すことができます。
jest.mock('path/to/MyNativeComponent', () => {
const mockComponent = require('react-native/jest/mockComponent');
return mockComponent('path/to/MyNativeComponent');
});
もしくは、あなたがマニュアルモックを作りたい場合は以下のようにして作ることができます。
jest.mock('Text', () => {
const RealComponent = jest.requireActual('Text');
const React = require('react');
class Text extends React.Component {
render() {
return React.createElement('Text', this.props, this.props.children);
}
}
Text.propTypes = RealComponent.propTypes;
return Text;
});
別のケースとして、React コンポーネントでないネイティブモジュールをモックしたいケースがあります。 そのケースでも同じテクニックが使えます。 ネイティブモジュールのソースコードをよく調べて、実デバイス上でアプリを実際に動かしながらモジュールのログを取って、マニュアルモックを設計することをおすすめします。
もしあなたが同じモジュールを何度もモックすることになったら、そのモックは別ファイルに定義してそれを setupFiles
リストに追加することをおすすめします。