React Native アプリのテスト
Facebookでは、Jestを使用してReact Nativeアプリケーションをテストしています。
実際に動作するReact Nativeアプリのテスト例について、より深く理解するには、次のシリーズをご覧ください。パート1:Jest - スナップショットの活用とパート2:Jest - アクションとレデューサーのためのReduxスナップショット。
セットアップ
react-nativeバージョン0.38以降、react-native init
を実行すると、デフォルトでJestのセットアップが含まれています。次の設定がpackage.jsonファイルに自動的に追加されます。
{
"scripts": {
"test": "jest"
},
"jest": {
"preset": "react-native"
}
}
Jestでテストを実行するには、yarn test
を実行します。
react-nativeアプリケーションをアップグレードしていて、以前にjest-react-native
プリセットを使用していた場合は、package.json
ファイルから依存関係を削除し、プリセットをreact-native
に変更してください。
スナップショットテスト
いくつかのビューとテキストコンポーネント、そしていくつかのスタイルを持つ小さなイントロコンポーネントのスナップショットテストを作成してみましょう。
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のスナップショット機能を使用して、コンポーネントと対話してレンダリングされた出力をキャプチャし、スナップショットファイルを作成してみましょう。
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
を実行すると、次のような出力ファイルが生成されます。
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>
`;
次回テストを実行すると、レンダリングされた出力が以前に作成されたスナップショットと比較されます。スナップショットはコードの変更と共にコミットする必要があります。スナップショットテストが失敗した場合、それが意図した変更か意図しない変更かを調査する必要があります。変更が予期される場合は、jest -u
でJestを呼び出して既存のスナップショットを上書きできます。
この例のコードは、examples/react-nativeにあります。
プリセット設定
プリセットは環境をセットアップし、非常に意見が強く、Facebookで役立つとわかったものに基づいています。すべての設定オプションは、プリセットを使用しない場合にカスタマイズできるのと同じように上書きできます。
環境
react-native
にはJestプリセットが付属しているため、package.json
のjest.preset
フィールドはreact-native
を指している必要があります。プリセットは、React Nativeアプリの環境を模倣したノード環境です。DOMまたはブラウザAPIを読み込まないため、Jestの起動時間が大幅に短縮されます。
transformIgnorePatternsのカスタマイズ
transformIgnorePatterns
オプションを使用して、Babelによって変換されるファイルを指定できます。残念ながら、多くのreact-native
npmモジュールは、公開前にソースコードをプリコンパイルしていません。
デフォルトでは、jest-react-native
プリセットは、プロジェクト独自のソースファイルとreact-native
のみを処理します。変換する必要があるnpm依存関係がある場合は、react-native
以外のモジュールを含めて、それらをグループ化し、|
演算子で区切ることで、この設定オプションをカスタマイズできます。
{
"transformIgnorePatterns": [
"node_modules/(?!(react-native|my-project|react-native-button)/)"
]
}
このようなツールを使用して、どのパスが一致するか(したがって変換から除外されるか)をテストできます。
transformIgnorePatterns
は、パスが提供されたいずれかのパターンと一致する場合、ファイルを変換から除外します。したがって、複数のパスに分割すると、注意しないと意図しない結果が生じる可能性があります。以下の例では、foo
とbar
の除外(負の先読みアサーションとも呼ばれます)が互いに打ち消し合っています。
{
"transformIgnorePatterns": ["node_modules/(?!foo/)", "node_modules/(?!bar/)"] // not what you want
}
setupFiles
すべてのテストファイルに追加の設定を提供する場合、setupFiles
設定オプションを使用してセットアップスクリプトを指定できます。
moduleNameMapper
moduleNameMapper
を使用して、モジュールパスを別のモジュールにマップできます。デフォルトでは、プリセットはすべての画像を画像スタブモジュールにマップしますが、モジュールが見つからない場合は、この設定オプションが役立ちます。
{
"moduleNameMapper": {
"my-module.js": "<rootDir>/path/to/my-module.js"
}
}
ヒント
jest.mockを使用してネイティブモジュールをモックする
react-native
に組み込まれているJestプリセットには、react-nativeリポジトリに適用されるいくつかのデフォルトモックが付属しています。ただし、一部のreact-nativeコンポーネントまたはサードパーティコンポーネントは、レンダリングされるネイティブコードに依存しています。このような場合、Jestの手動モッキングシステムは、基礎となる実装をモックアウトするのに役立ちます。
たとえば、コードがreact-native-video
と呼ばれるサードパーティのネイティブビデオコンポーネントに依存している場合、次のような手動モックでスタブアウトすることができます。
jest.mock('react-native-video', () => 'Video');
これにより、コンポーネントはスナップショット出力にすべてのプロパティを持つ<Video {...props} />
としてレンダリングされます。EnzymeとReact 16に関する注意事項も参照してください。
場合によっては、より複雑な手動モックを提供する必要があります。たとえば、ネイティブコンポーネントのプロパティタイプまたは静的フィールドをモックに転送する場合、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コンポーネントではないネイティブモジュールをモックする場合もあります。同じテクニックを適用できます。ネイティブモジュールのソースコードを調べて、実際のデバイスでreact nativeアプリを実行しているときにモジュールをログに記録し、実際のモジュールに基づいて手動モックをモデル化することをお勧めします。
同じモジュールを何度もモックすることになる場合は、これらのモックを別のファイルで定義し、setupFiles
のリストに追加することをお勧めします。