ReactNativeアプリケーションでのユニットテスト

公開: 2022-03-10
簡単な要約↬単体テストは、ソフトウェア開発プロセスの不可欠な部分になっています。 これは、ソフトウェアのコンポーネントがテストされるテストのレベルです。 このチュートリアルでは、ReactNativeアプリケーションのユニットをテストする方法を学習します。

React Nativeは、モバイルアプリケーションを構築するために最も広く使用されているフレームワークの1つです。 このチュートリアルは、作成したReactNativeアプリケーションのテストを開始したい開発者を対象としています。 JestテストフレームワークとEnzymeを利用します。

この記事では、テストのコア原則を学び、アプリケーションをテストするためのさまざまなライブラリーを探索し、React Nativeアプリケーションの単体(またはコンポーネント)をテストする方法を確認します。 React Nativeアプリケーションを使用することで、テストに関する知識を固めることができます。

注: JavaScriptとReact Nativeの基本的な知識は、このチュートリアルを進める上で非常に役立ちます。

ユニットテストとは何ですか?

単体テストは、ソフトウェアの個々のコンポーネントがテストされるテストのレベルです。 各コンポーネントが期待どおりに機能することを確認するために行います。 コンポーネントは、ソフトウェアのテスト可能な最小の部分です。

説明のために、 Buttonコンポーネントを作成し、単体テストをシミュレートしてみましょう。

 import React from 'react'; import { StyleSheet, Text, TouchableOpacity } from 'react-native'; function AppButton({ onPress }) { return ( <TouchableOpacity style={[styles.button, { backgroundColor: colors[color] }]} onPress={onPress} > <Text style={styles.text}>Register</Text> </TouchableOpacity> ); } const styles = StyleSheet.create({ button: { backgroundColor: red; borderRadius: 25, justifyContent: 'center', alignItems: 'center', }, text: { color: #fff } }) export default AppButton;

このButtonコンポーネントには、テキストとonPress関数があります。 このコンポーネントをテストして、単体テストの内容を確認しましょう。

まず、 Button.test.jsという名前のテストファイルを作成しましょう。

 it('renders correctly across screens', () => { const tree = renderer.create(<Button />).toJSON(); expect(tree).toMatchSnapshot(); });

ここでは、 Buttonコンポーネントがアプリケーションのすべての画面で正しくレンダリングされるかどうかをテストしています。 これが単体テストのすべてです。アプリケーションのコンポーネントをテストして、正常に機能することを確認します。

ReactNativeアプリケーションでのユニットテスト

React Nativeアプリケーションは、さまざまなツールでテストできます。その一部は次のとおりです。

  • WebDriver
    Node.jsアプリ用のこのオープンソーステストツールは、ReactNativeアプリケーションのテストにも使用されます。
  • 悪夢
    これにより、ブラウザでのテスト操作が自動化されます。 ドキュメントによると、「目標は、深くネストされたコールバックではなく、スクリプトの各ブロックに対して同期していると感じるAPIを使用して、ユーザーアクション( gototypeclickなど)を模倣するいくつかの単純なメソッドを公開することです。」
  • 冗談
    これは、世の中で最も人気のあるテストライブラリの1つであり、今日焦点を当てるライブラリです。 Reactと同様に、Facebookによって維持され、最大のパフォーマンスを得るために「ゼロ構成」セットアップを提供するために作成されました。
  • モカ
    Mochaは、ReactおよびReactNativeアプリケーションをテストするための人気のあるライブラリです。 セットアップと使用が非常に簡単で、高速であるため、開発者が選択するテストツールになりました。
  • ジャスミン
    そのドキュメントによると、JasmineはJavaScriptコードをテストするためのビヘイビア駆動開発フレームワークです。
ジャンプした後もっと! 以下を読み続けてください↓

ジェストと酵素の紹介

そのドキュメントによると、「Jestはシンプルさに焦点を当てた楽しいJavaScriptテストフレームワークです」。 ゼロ構成で動作します。 インストール時に(npmやYarnなどのパッケージマネージャーを使用して)、Jestを使用する準備が整い、他のインストールは必要ありません。

Enzymeは、ReactNativeアプリケーション用のJavaScriptテストフレームワークです。 (React NativeではなくReactを使用している場合は、ガイドを利用できます。)Enzymeを使用して、アプリケーションの出力の単位をテストします。 これにより、アプリケーションの実行時間をシミュレートできます。

プロジェクトの設定から始めましょう。 GitHubのDoneWithItアプリを使用します。 これはReactNativeアプリケーションマーケットプレイスです。 クローンを作成することから始め、フォルダーに移動し、npmに対して次のコマンドを実行してパッケージをインストールします…

 npm install

…またはこれは糸の場合:

 yarn install

このコマンドは、アプリケーションにすべてのパッケージをインストールします。 それが完了したら、以下で説明するスナップショットを使用して、アプリケーションのUIの整合性をテストします。

スナップショットとJest構成

このセクションでは、Jestを使用してスナップショットをテストすることにより、ユーザーのタッチとアプリのコンポーネントのUIをテストします。

その前に、Jestとその依存関係をインストールする必要があります。 Jest for Expo React Nativeをインストールするには、次のコマンドを実行します。

 yarn add jest-expo --dev

これにより、 jest-expoがアプリケーションのディレクトリにインストールされます。 次に、 package.jsonファイルを更新してテストスクリプトを作成する必要があります。

 "scripts": { "test" "jest" }, "jest": { "preset": "jest-expo" }

このコマンドを追加することで、アプリケーションに登録するパッケージと場所をJestに指示します。

次に、Jestが包括的なテストを行うのに役立つ他のパッケージをアプリケーションに追加します。 npmの場合、これを実行します…

 npm i react-test-renderer --save-dev

…そしてYarnの場合、これは:

 yarn add react-test-renderer --dev

package.jsonファイルで行うべき設定がまだ少しあります。 Expo React Nativeのドキュメントによると、ソースファイルがテストと一致するたびに(つまり、テストが行​​われ、プロジェクトのnode modulesで同様のファイルが見つかった場合)、Jestでテストが実行されないようにするtransformIgnorePattern構成を追加する必要があります。

 "jest": { "preset": "jest-expo", "transformIgnorePatterns": [ "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)" ] }

それでは、最初のテストを作成するために、 App.test.jsという名前の新しいファイルを作成しましょう。 Appのツリーに子要素が1つあるかどうかをテストします。

 import React from "react"; import renderer from "react-test-renderer"; import App from "./App.js" describe("<App />", () => { it('has 1 child', () => { const tree = renderer.create(<App />).toJSON(); expect(tree.children.length).toBe(1); }); });

次に、 yarn testまたはそれに相当するnpmを実行します。 App.jsに子要素が1つある場合、テストに合格する必要があります。これは、コマンドラインインターフェイスで確認されます。

上記のコードでは、 Reactreact-test-rendererをインポートしました。これにより、 Expoのテストがレンダリングされます。 <App />コンポーネントツリーをJSONに変換してから、JSONで返される子コンポーネントの数が予想と等しいかどうかをJestに確認するように依頼しました。

その他のスナップショットテスト

David Adeneyeが述べているように:

「スナップショットテストでは、Webアプリケーションのユーザーインターフェイス(UI)が予期せず変更されていないことを確認します。 ある瞬間のコンポーネントのコードをキャプチャするので、ある状態のコンポーネントを、他の可能な状態と比較することができます。」

これは、プロジェクトに多くのコンポーネントで使用されるグローバルスタイルが含まれる場合に特に行われます。 App.jsのスナップショットテストを作成して、UIの整合性をテストしてみましょう。

 it('renders correctly across screens', () => { const tree = renderer.create( ).toJSON(); expect(tree).toMatchSnapshot(); }); it('renders correctly across screens', () => { const tree = renderer.create( ).toJSON(); expect(tree).toMatchSnapshot(); });

これをすでに作成したテストに追加してから、 yarn test (またはそれに相当するnpm)を実行します。 テストに合格すると、次のように表示されます。

 PASS src/App.test.js √ has 1 child (16ms) √ renders correctly (16ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 1 total Time: 24s

これは、テストに合格し、テストにかかった時間を示しています。 テストに合格した場合、結果は同じように見えます。

Jestのいくつかの機能のモックに移りましょう。

API呼び出しのモック

Jestのドキュメントによると:

モック関数を使用すると、関数の実際の実装を消去し、関数への呼び出し(およびそれらの呼び出しで渡されるパラメーター)をキャプチャし、 `new`でインスタンス化されたときにコンストラクター関数のインスタンスをキャプチャし、テストを許可することで、コード間のリンクをテストできます。戻り値の時間構成。

簡単に言えば、モックは、その関数の実際の動作を含まないオブジェクトまたは関数のコピーです。 それはその機能を模倣します。

モックはさまざまな方法でアプリをテストするのに役立ちますが、主な利点は、依存関係の必要性が減ることです。

モックは通常、2つの方法のいずれかで実行できます。 1つは、テストするコードに挿入されるモック関数を作成することです。 もう1つは、コンポーネントにアタッチされているパッケージまたは依存関係をオーバーライドするモック関数を作成することです。

ほとんどの組織や開発者は、機能を模倣し、偽のデータを使用して一部のコンポーネントをテストする手動モックを作成することを好みます。

React Nativeには、グローバルオブジェクトにfetchが含まれています。 単体テストで実際のAPI呼び出しを行わないようにするために、それらをモックします。 以下は、React Nativeで、依存関係を必要とせずに、ほとんどではないにしてもすべてのAPI呼び出しをモックする方法です。

 global.fetch = jest.fn(); // mocking an API success response once fetch.mockResponseIsSuccess = (body) => { fetch.mockImplementationForOnce ( () => Promise.resolve({json: () => Promise.resolve(JSON.parse(body))}) ); }; // mocking an API failure response for once fetch.mockResponseIsFailure = (error) => { fetch.mockImplementationForOnce( () => Promise.reject(error) ); };

ここでは、APIを1回フェッチしようとする関数を作成しました。 これを実行すると、promiseが返され、解決されると、JSONで本文が返されます。 これは、失敗したフェッチトランザクションの模擬応答に似ています—エラーを返します。

以下は、アプリケーションのproductコンポーネントであり、 productオブジェクトを含み、情報をpropsとして返します。

 import React from 'react'; const Product = () => { const product = { name: 'Pizza', quantity: 5, price: '$50' } return ( <> <h1>Name: {product.name}</h1> <h1>Quantity: {product.quantity}</h1> <h1>Price: {product.price}</h1> </> ); } export default Product;

製品のすべてのコンポーネントをテストしようとしていると想像してみましょう。 私たちのデータベースに直接アクセスすることは実行可能な解決策ではありません。 ここでモックが登場します。 以下のコードでは、Jestを使用してコンポーネント内のオブジェクトを記述し、製品のコンポーネントをモックしようとしています。

 describe("", () => { it("accepts products props", () => { const wrapper = mount(<Customer product={product} />); expect(wrapper.props().product).toEqual(product); }); it("contains products quantity", () => { expect(value).toBe(3); }); });

実行したいテストを指示するために、Jestのdescribeを使用しています。 最初のテストでは、渡すオブジェクトが、モックした小道具と等しいかどうかを確認しています。

2番目のテストでは、 customerの小道具に合格して、それが製品であり、モックと一致することを確認します。 そうすることで、製品のすべてのコンポーネントをテストする必要がなくなり、コードのバグを防ぐこともできます。

外部APIリクエストのモック

これまで、アプリケーション内の他の要素を使用してAPI呼び出しのテストを実行してきました。 次に、外部API呼び出しをモックしてみましょう。 Axiosを使用します。 APIへの外部呼び出しをテストするには、リクエストをモックし、取得したレスポンスを管理する必要があります。 axios-mock-adapterを使用してAxiosをモックします。 まず、以下のコマンドを実行してaxios-mock-adapterをインストールする必要があります。

 yarn add axios-mock-adapter

次に行うことは、モックを作成することです。

 import MockAdapter from 'axios-mock-adapter'; import Faker from 'faker' import ApiClient from '../constants/api-client'; import userDetails from 'jest/mockResponseObjects/user-objects'; let mockApi = new MockAdapter(ApiClient.getAxiosInstance()); let validAuthentication = { name: Faker.internet.email(), password: Faker.internet.password() mockApi.onPost('requests').reply(config) => { if (config.data === validAuthentication) { return [200, userDetails]; } return [400, 'Incorrect username and password']; });

ここでは、 ApiClientを呼び出し、Axiosインスタンスを渡して、ユーザーの資格情報をモックします。 メールアドレスやパスワードなどの偽のユーザーデータを生成するために、faker.jsという名前のパッケージを使用しています。

モックは、APIが期待するとおりに動作します。 リクエストが成功すると、ステータスコード200のOKのレスポンスが返されます。 また、サーバーへの不正なリクエストに対してステータスコード400を取得します。これは、「ユーザー名とパスワードが正しくありません」というメッセージとともにJSONで送信されます。

モックの準備ができたので、外部APIリクエストのテストを作成しましょう。 以前と同様に、スナップショットを使用します。

 it('successful sign in with correct credentials', async () => { await store.dispatch(authenticateUser('[email protected]', 'password')); expect(getActions()).toMatchSnapshot(); }); it('unsuccessful sign in with wrong credentials', async () => { await store.dispatch(authenticateUser('[email protected]', 'wrong credential')) .catch((error) => { expect(errorObject).toMatchSnapshot(); });

ここでは、入力を保持するためにネイティブJavaScript async awaitを使用して、正しいクレデンシャルで正常にサインインできるかどうかをテストしています。 一方、JestのauthenticateUser関数はリクエストを認証し、それが以前のスナップショットと一致することを確認します。 次に、電子メールアドレスやパスワードなどの資格情報が間違っている場合にサインインが失敗するかどうかをテストし、応答としてエラーを送信します。

次に、 yarn testまたはnpm testを実行します。 すべてのテストに合格すると確信しています。

状態管理ライブラリReduxでコンポーネントをテストする方法を見てみましょう。

スナップショットを使用したReduxアクションとレデューサーのテスト

ReduxがReactアプリケーションで最も広く使用されている状態マネージャーの1つであることは否定できません。 Reduxのほとんどの機能には、アプリケーションの状態の変更をトリガーするために使用されるReduxストアの機能であるdispatchが含まれます。 Reduxのactionsはサイズと複雑さが急速に増大するため、Reduxのテストは難しい場合があります。 Jestスナップショットを使用すると、これが簡単になります。 Reduxを使用したほとんどのテストは、次の2つに分類されます。

  • actionsをテストするために、 redux-mock-storeを作成してアクションをディスパッチします。
  • レデューサーをテストするには、 reducerをインポートして、状態とアクションオブジェクトを渡します。

以下はスナップショットを使用したReduxテストです。 SIGN-IN時にユーザーを認証し、 userリデューサーによってLOGOUTアクションがどのように処理されるかを確認することにより、ディスパッチされたアクションをテストします。

 import mockStore from 'redux-mock-store'; import { LOGOUT } from '../actions/logout'; import User from '../reducers/user'; import { testUser } from 'jest/mock-objects'; describe('Testing the sign in authentication', () => { const store = mockStore(); it('user attempts with correct password and succeeds', async () => { await store.dispatch(authenticateUser('[email protected]', 'password')); expect(store.getActions()).toMatchSnapshot(); }); }); describe('Testing reducers after user LOGS OUT', () => { it('user is returned back to initial app state', () => { expect(user(testUser, { type: LOGOUT })).toMatchSnapshot(); }); });

最初のテストでは、サインイン認証とモックストアの作成について説明します。 これを行うには、最初にReduxからmockStoreをインポートし、次にJestからtestUserという名前のメソッドをインポートして、ユーザーのモックを作成します。 次に、スナップショットストアにあるものと一致する電子メールアドレスとパスワードを使用して、ユーザーがアプリケーションに正常にサインインしたときをテストします。 したがって、スナップショットは、テストが実行されるたびに、ユーザーが入力しているオブジェクトが一致することを保証します。

2番目のテストでは、ユーザーがいつログアウトするかをテストしています。 レデューサースナップショットは、ユーザーがログアウトしたことを確認すると、アプリケーションの初期状態に戻ります。

次に、 yarn testを実行してテストします。 テストに合格すると、次の結果が表示されます。

 PASS src/redux/actions.test.js √ user attempts with correct password and succeeds (23ms) √ user is returned back to initial app state (19ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 2 total Time: 31s

結論

Jestを使用すると、React Nativeアプリケーションのテストがこれまでになく簡単になります。特にスナップショットを使用すると、グローバルスタイルに関係なくUIの一貫性が保たれます。 また、Jestを使用すると、アプリケーションで特定のAPI呼び出しとモジュールをモックすることができます。 React Nativeアプリケーションのコンポーネントをテストすることで、これをさらに進めることができます。

その他のリソース

  • 「ReactNativeアプリケーションをJestでテストするための実用ガイド」、David Adeneye、Smashing Magazine
  • ジェストのドキュメント
  • 「TestingWithJest」、Expo ReactNativeのドキュメント
  • 「JestでReactNativeをテストする方法を学ぶ」、Jason Gaare