ReactNative + Expoの開発ビルドをDetoxでE2Eテストする
はじめに
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を書き換えます
"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を書き換えます
},
"web": {
"favicon": "./assets/favicon.png"
- }
+ },
+ "plugins": [
+ [
+ "@config-plugins/detox",
+ {
+ "skipProguard": true
+ }
+ ]
+ ]
}
}
ここまでできたら一度prebuildをして、ビルドファイルを作成しておきます。
$ npx expo prebuild
次にpackage.jsonを変更してテスト用スクリプトを追加します。このあたりはドキュメントにはのってないので、config-pluginsレポジトリ内のappを参考に作ります。(リンク)。
- "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用ビルドに対してのみテストを行うようにします。
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を変更します。
<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を変更しテストが通るようにします。
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