🧑‍💻

ReactNative + Expoの開発ビルドをDetoxでE2Eテストする

2022/09/14に公開

はじめに

React Native + Expoで作成したアプリをDetoxでE2Eテストします。
expo-dev-clientの導入もしています。
また本記事の目標は、一旦ローカルでのE2Eテスト実行としています(CIでの実行についても必要に応じて検証次第記述したいと思います)

やっていく

1. プロジェクトの作成

プロジェクトを作成し、Detox用のパッケージとテスト用のパッケージをインストールします。

$ npx create-expo-app -t expo-template-blank-typescript
✔ What is your app named? expo-detox

$ cd expo-detox

2. expo-dev-clientを導入する

Expoでexpo-dev-clientを使うための設定をします(リンク)。

npx expo install expo-dev-client

package.jsonを書き換えます

package.json
   "scripts": {
-    "start": "expo start",
+    "start": "expo start --dev-client",

3. Detoxのインストール

今回は、Jestを使います。

$ npm install -D detox-cli detox
 # (バージョン依存で怒られたので28バージョン指定しています)
$ npm install -D @types/jest@28 babel-jest@28 jest@28 jest-circus@28 ts-jest@28 jest-config@28

# これに加えてapplesimutilsやAndroid SDKの設定も必要。ここでは省略

初期ファイルも追加します。

$ npx detox init -r jest

4. ExpoでDetox用の設定をする

Detox用の設定を少し変更したいので、expo/config-pluginsのdedoxパッケージを使用します。この設定をしていきます(参考:リンクリンク)。

$ npm install -D @config-plugins/detox

app.jsonを書き換えます

app.json
     },
     "web": {
       "favicon": "./assets/favicon.png"
-    }
+    },
+    "plugins": [
+      [
+        "@config-plugins/detox",
+        {
+          "skipProguard": true
+        }
+      ]
+    ]
   }
 }

ここまでできたら一度prebuildをして、ビルドファイルを作成しておきます。

$ npx expo prebuild

次にpackage.jsonを変更してテスト用スクリプトを追加します。このあたりはドキュメントにはのってないので、config-pluginsレポジトリ内のappを参考に作ります。(リンク)。

package.json
-    "web": "expo start --web"
+    "web": "expo start --web",
+
+    "build:ios": "detox build -c ios",
+    "test:ios": "detox test -c ios",
+    "e2e:ios": "npm run build:ios && npm run test:ios",
+
+    "build:android": "detox build -c android",
+    "test:android": "detox test -c android",
+    "e2e:android": "npm run build:android && npm run test:android",
+    "clean:android": "pushd android && ./gradlew clean && popd"
   },

次に設定ファイルを新規に追加します。変数を定義したいので、jsでの設定ファイルを作ります。

$ touch detox.config.js
# 古いのは削除
$ rm .detoxrc.json

detox.config.jsファイルを以下のようにしてください。定義している変数はご自身の環境に合わせてください。参考リンク上では、ReleaseとDebugの両オプションを設定していますが、expo-dev-clientを導入した状態のDebugビルドだと開発用ダッシュボードが表示されてしまうので、Release用ビルドに対してのみテストを行うようにします。

detox.config.js
const app = 'expodetox'
const simulator = "iPhone 14"
const emulator = "Pixel_3a_API_30"

module.exports = {
  testRunner: "jest",
  runnerConfig: "e2e/config.json",
  skipLegacyWorkersInjection: true,
  apps: {
    "ios": {
      type: "ios.app",
      binaryPath: `ios/build/Build/Products/Release-iphonesimulator/${app}.app`,
      build: `xcodebuild -workspace ios/${app}.xcworkspace -scheme ${app} -configuration Release -sdk iphonesimulator -derivedDataPath ios/build -UseModernBuildSystem='NO' | npx excpretty ./`
    },
    "android": {
      type: "android.apk",
      binaryPath: "android/app/build/outputs/apk/release/app-release.apk",
      build: "pushd android; ./gradlew app:assembleRelease app:assembleAndroidTest -DtestBuildType=release; popd"
    }
  },
  devices: {
    simulator: {
      type: "ios.simulator",
      device: {
        type: `${simulator}`
      }
    },
    emulator: {
      type: "android.emulator",
      device: {
        avdName: `${emulator}`
      }
    }
  },
  configurations: {
    "ios": {
      device: "simulator",
      app: "ios"
    },
    "android": {
      device: "emulator",
      app: "android"
    }
  }
}

以上で、設定は完了です。一度ビルドスクリプトを実行し、きちんとビルドできるか確かめます。

$ npm run build:ios

...
› Creating  expodetox » expodetox.app
› Build Succeeded

› 0 error(s), and 5 warning(s)

$ npm run build:android

続いてE2Eテストを書いて実行していきます

E2Eテストを実行する

簡単にテストを書いて実行します
App.tsxを変更します。

App.tsx
     <View style={styles.container}>
-      <Text>Open up App.tsx to start working on your app!</Text>
+      <Text testID="homeText">Hello World!!!</Text>
       <StatusBar style="auto" />
     </View>
   );

firstTest.e2e.jsを変更しテストが通るようにします。

e2e/firstTest.e2e.js
describe('Example', () => {
  beforeAll(async () => {
    await device.launchApp();
  });

  beforeEach(async () => {
    await device.reloadReactNative();
  });

  it('should have welcome screen', async () => {
    await expect(element(by.id('homeText'))).toBeVisible();
    await expect(element(by.text('Hello World!!!'))).toBeVisible();
  });
});

iOSでE2Eテストを実行します

# Simulatorを立てておく
$ npm run start
# 別ターミナルで実行する
$ npm run e2e:ios
 PASS  e2e/firstTest.e2e.js (7.138 s)
  Example
    ✓ should have welcome screen (250 ms)

andriodでE2Eテストを実行します

# Emulatorを立てておく
$ npm run e2e:android
$ PASS  e2e/firstTest.e2e.js
  Example
    ✓ should have welcome screen (159 ms)

まとめ・感想

Detoxを使ったE2Eテストを構築しました。

これから実運用というフェーズなので他にまだ罠があるかもしれません。随時追記していきたいと思います。また間違い等があればコメントいただけると幸いです。

今回のコードはこちらのレポジトリにおいています

Discussion