Expo アプリでの Detox を使った End-to-End テストの実装
タイトル通り「Detox を使った End-to-End テストの実装」 についての紹介です。
基本的にはドキュメント通りに進めれば実行可能です。しかし Expo を使ったアプリの場合、うまく動かない現象に遭遇しました。 その解決方法も紹介します。
概要
- Detox とは
- インストール
- Expo アプリの場合に必要なもの
- テスト実行
- エラーがでてテストが正常に動作しない
- 解決方法
- Detox の簡単な使い方
Detox とは
End-to-End テストを実装するためのパッケージです。
End-to-End テストとは、ユーザーインターフェーステストとも呼ばれる物です。システム全体に対してユーザーが実際に動かす操作を行い、期待した動作になっていることを確認します。 スナップショットテストやユニットテストとは異なり、実際にアプリをシミュレータ上で動かして操作を行い期待した動作になることをテストします。
より本番環境に近い形でのテストですね。シミュレータ上で行うので CI でのテスト実行も可能です。
インストール
まずは必要なパッケージをインストールします。 いつも通り npm・yarn で、テストでのみ使うので dev にインストールします。
npm install --save-dev detox detox-expo-helpers expo-detox-hook
Detox ではシミュレータを操作するので、必要なツールを brew でインストールします。
brew install --HEAD applesimutils
あとはコマンドラインで操作できるようにcliツールをグローバルにインストールしておきます。
npm install -g detox-cli
インストールが完了した後は以下のコマンドで Detox の初期設定を行うことができます。
detox init -r jest
e2e/
以下に設定ファイルやテストケースが作成されます。
Expo アプリの場合に必要なもの
上記の init した時点で必要な設定やファイルが一式生成されます。 Expo でテストを行う場合、以下の対応も必要になります。
reloadApp
に書き換え
初期の状態ではdevice.reloadReactNative()
を各テストの前に呼んでいます。 これをdetox-expo-helpers
のreloadApp
に変更する必要があります。
const { reloadApp } = require('detox-expo-helpers');
~~~
describe('Example', () => {
beforeEach(async () => {
await reloadApp();
});
~~~
- Expo go App を用意する
さらに、シミュレータで動かすにあたり Expo go アプリの app ソースも必要になります。
Expo go のタウンロードページにて、IPAファイルを直接ダウンロードして解凍を行います。そのディレクトリをExpo.app
という名前にリネームしておきます。
プロジェクトのディレクトリにbin/
などのディレクトリを用意して上記 Expo.app を設置しておきます。
その後 Detox の設定ファイル detoxrc.json
に以下を追加します。configurations にて使用できるシミュレータを追加しています。name などは自分の使っているシミュレータに合わせて設定しましょう。
"configurations": {
"ios.sim": {
"binaryPath": "bin/Expo.app",
"type": "ios.simulator",
"name": "iPhone 11"
},
}
テスト実行
テストを実行する際には Expo でエミュレータ上にアプリを起動しておき、以下のコマンドを実行します。 オプションで先程設定したシミュレータを使用するように設定しています。
detox test --configuration ios.sim
すると Detox でのE2Eテストが動き始めます。
エラーがでてテストが正常に動作しない
しかし、私の場合は最初のアプリのリロードは行われるのですが、以降の操作が実行されずテストの項目もチェックされない状態になっていました。 ただタイムアウトしてテストが失敗する状態です。
テスト結果には以下の出力がありました。
App has not responded to the network requests below:
(id = -1000) isReady: {}
どうやら、detox-expo-helpers
の reloadApp
を実行した際に、アプリの再起動が完了したことをリッスンしているのですが、その信号をキャッチできていないとのこと。
なので、アプリが起動しているにもかかわらずテストのチェックや操作が動き出さずそのままタイムアウトしています。
解決方法
解決方法を探したところ以下のissue にてやりとりされていました。
https://github.com/expo/detox-tools/issues/1
ざっくりの解決方法の方針としては、上記の内容を参考に以下のように行いました。
reloadApp
にてアプリの起動完了チェックを行わない- アプリの起動完了チェックを自前で行いテストを始める
それぞれの詳細な対応について私の対応方法を以下に記載します。
reloadApp
にてアプリの起動完了チェックを行わない
detox-expo-helpers
に対して patch-package
を使って、起動完了チェックを無効化するパッチを作成。
以下パッチの中身です。detox-expo-helpers+0.6.0.patch
diff --git a/node_modules/detox-expo-helpers/index.js b/node_modules/detox-expo-helpers/index.js
index 864493b..5de0839 100644
--- a/node_modules/detox-expo-helpers/index.js
+++ b/node_modules/detox-expo-helpers/index.js
@@ -70,7 +70,7 @@ const reloadApp = async (params) => {
newInstance: true,
url,
sourceApp: 'host.exp.exponent',
- launchArgs: { EXKernelDisableNuxDefaultsKey: true, detoxURLBlacklistRegex: formattedBlacklistArg },
+ launchArgs: { EXKernelDisableNuxDefaultsKey: true, detoxURLBlacklistRegex: formattedBlacklistArg, detoxEnableSynchronization: 0 },
});
アプリの起動完了チェックを自前で行いテストを始める
テストケースのreloadApp
が起動完了のチェックを行わなくなったので、直後に起動を確認する処理を追加します。
アプリの起動後に開く画面、ホーム画面などにtestID={"container"}
のコンポーネントを用意しておき、それが表示されたことをチェックしています。
beforeAll(async () => {
await reloadApp();
await waitFor(element(by.id("container")))
.toBeVisible()
.withTimeout(4000);
});
~~~
上記対応で、アプリの再読み込みと起動完了のチェックが毎テストの前に正常に行われるようになりました。
テストも正常にシミュレータが動き、テストケースが順番通り実行されました。
Detox の簡単な使い方
さまざまなAPIがありますが、簡単なものだけ紹介します。
it("should have FAB", async () => {
await expect(element(by.id("container"))).toBeVisible();
await expect(element(by.id("FAB"))).toBeVisible();
});
testID で要素を検出して、toBeVisible
で画面に描写されていることをテストします。
it("should show new memo screen after tap", async () => {
await expect(element(by.id("FAB"))).toBeVisible();
await element(by.id("FAB")).tap();
await expect(element(by.text("create new memo"))).toBeVisible();
});
testID で要素を検出して、tap
で要素のタップイベントを実行できます。
シミュレータ上でも動作が行われ、その後の画面の要素の描写を確認することで、画面遷移などが行われたことをテストできます。
おわりに
Expo の場合はいくつか追加の設定が必要になりましたが、無事に Detox を利用できました。
エラーも発生したので Detox 使えないのかもと思いましたが、なんとか動かせました。
detox-expo-helpers
の問題の様子?なので、いつか修正されるのを待ちましょう。修正されるまではとりあえずパッチを当てて確認するのがよいです。
E2Eテストが実行できるとインターフェース面でのテストが豊富に実施できるので、とても便利です。 ローカル環境だけでなくCI環境でもテストを動かせるようにして、変更のチェックを行えるとさらに便利になります。