Table of Contents
Maestroとは
Maestroは、最もシンプルで効果的なモバイルUIテストフレームワークです。モバイルのテストフレームワークでは、appniumやdetoxなどがありますが、セットアップが簡単、かつ、テストパターンをGUIで作成出来たりと簡単に強力なテスト環境を作成できるフレームワークです。テストはyaml形式で定義出来ます。 詳しくは、公式サイトをご確認ください。
Maestroのセットアップ
セットアップはローカルにMaestroをインストールし、iOSに必要なライブラリをインストールすれば完了します。 公式ドキュメントどおりに、実施すれば特に躓くことなく環境構築出来ました。
Maestroのインストール
curl -Ls "https://get.maestro.mobile.dev" | bash
iOSのテストに必要なライブラリのインストール
brew tap facebook/fb
brew install facebook/fb/idb-companion
実際にテストする
今回は、簡単なログイン画面とログイン後のHome画面を用意して、実際にmaestroのe2eテストを試してみます。
アプリ側のコードはこんな感じです。
ログイン画面
import React, { useState } from "react";
import {
StyleSheet,
Text,
TextInput,
TouchableOpacity,
Alert,
} from "react-native";
import { router, useLocalSearchParams } from "expo-router";
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const { isLogout } = useLocalSearchParams();
const handleLogin = () => {
if (email.length === 0 || password.length === 0) {
Alert.alert("エラー", "メールとパスワードを入力してください。");
} else {
router.replace({ pathname: "/Home", params: { email } });
}
};
return (
<>
<Text style={styles.title}>ログイン</Text>
<TextInput
style={styles.input}
placeholder="メールアドレス"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholder="パスワード"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
{isLogout && <Text style={{ color: "red" }}>ログアウトしました。</Text>}
<TouchableOpacity
style={styles.button}
onPress={handleLogin}
testID="loginButton"
>
<Text style={styles.buttonText}>ログイン</Text>
</TouchableOpacity>
</>
);
};
const styles = StyleSheet.create({
title: {
fontSize: 24,
fontWeight: "bold",
marginBottom: 20,
},
input: {
width: "80%",
paddingHorizontal: 10,
paddingVertical: 8,
borderColor: "#999",
borderWidth: 1,
borderRadius: 4,
marginBottom: 14,
fontSize: 18,
},
button: {
marginTop: 40,
backgroundColor: "#6200ee",
padding: 10,
borderRadius: 4,
width: "60%",
alignItems: "center",
},
buttonText: {
color: "#fff",
fontSize: 18,
fontWeight: "bold",
},
});
export default Login;
ホーム画面
import React from "react";
import { View, StyleSheet, Text, TouchableOpacity } from "react-native";
import { router, useLocalSearchParams } from "expo-router";
export default function Home() {
const params = useLocalSearchParams();
const { email } = params;
function handleLogin() {
router.replace({ pathname: "/", params: { isLogout: true } });
}
return (
<>
<Text style={styles.text}>{email}さんこんにちは</Text>
<TouchableOpacity style={styles.button} onPress={handleLogin}>
<Text style={styles.buttonText}>ログアウト</Text>
</TouchableOpacity>
</>
);
}
const styles = StyleSheet.create({
text: {
fontSize: 20,
marginBottom: 20,
},
button: {
marginTop: 40,
backgroundColor: "#6200ee",
padding: 10,
borderRadius: 4,
width: "60%",
alignItems: "center",
},
buttonText: {
color: "#fff",
fontSize: 18,
fontWeight: "bold",
},
});
全てのコードを確認する場合は、githubリポジトリをご確認ください。 それでは、テストコードを書いていきます。 といっても、コレだけです。
appId: host.exp.Exponent
---
- launchApp
- tapOn: "maestro"
- tapOn: "メールアドレス"
- inputText: "hoge@example.com"
- tapOn: "パスワード"
- inputText: "password"
- tapOn: "return"
- tapOn:
id: "loginButton"
- tapOn: "あとで行う"
- assertVisible: "hoge@example.comさんこんにちは"
- assertVisible: "ログアウト"
今回は、expo goのシミュレーターでテストを実施するため、host.exp.Exponent
としていますが、ビルド済みのアプリでテストする場合は、app.config.jsのios.bundleIdentifier
もしくは、 android.package
に置き換えてください。
テストは以下のコマンドで実行出来ます。
maestro test ${実行するテストファイル}
画面収録をしたい場合は、test→recordにするとテスト中の画面収録が出来ます。
テストを再利用する
login.yamlでログイン周りのe2eテストができるようになりました。 ログイン後正しくログアウトできるかのテストも追加したいと思います。 1つのテストが長くなりすぎると、視認性・メンテナンス性が悪くなるので、logoutTestを別で作ろうと思います。 このダミーアプリではログイン状態を永続化する処理がないので、本来であれば、logoutでもログインする処理を書く必要がありますが、 Maestroでは、既存のテストを再利用することが出来ます。
appId: host.exp.Exponent
---
- runFlow:
file: "login.yaml"
- tapOn: 'ログアウト'
- assertVisible: "ログアウトしました。"
テストに環境変数を使用する
ログインとログアウトのテストができるよになりました。 ただ、テストに使うアカウントを別のものを使用したくなった場合、login.yamlを複製し別のテストを作成するか、login.yamlを編集する必要があります。 login.yamlでメールアドレス・パスワードをハードコードするのではなく、環境変数に切り出し、実行時にテストに使用するアカウントを選択できるよに修正してみます。
appId: host.exp.Exponent
---
- launchApp
- tapOn: "maestro"
- tapOn: "メールアドレス"
- inputText: ${email}
- tapOn: "パスワード"
- inputText: ${password}
- tapOn: "return"
- tapOn:
id: "loginButton"
- assertVisible: "${email}さんこんにちは"
- assertVisible: "ログアウト"
実行時に環境変数を渡すことで、テストに使用するアカウントを選べるようになりました。
maestro test test/login.yaml -e email=foo@example.com -e password=pass
この変更により、login.yamlはlogout.yamlでも実行時に環境変数を指定する必要がでてきました。ただ、ログアウトのテストはどのアカウントで行っても同じ場合など、実行時に指定する必要がない場合は、logout.yamlでlogin.yamlの環境変数を指定する事ができます。
appId: host.exp.Exponent
---
- runFlow:
file: "login.yaml"
env:
email: user@example.com
password: 123
- tapOn: 'ログアウト'
- assertVisible: "ログアウトしました。"
条件に応じたテストをする
ログインテストのメールアドレス・パスワードを実行時に変更できるようになった影響で、ログイン後にキーチェーンへパスワードを保存するか確認するアラートが表示されテストが落ちるようになってしまいました。端末の状態によって本来落ちるべきでない部分でe2eテストが落ちるのはよろしくないので、こういった些末な部分に影響されないように修正します。
appId: host.exp.Exponent
---
- launchApp
- tapOn: "maestro"
- tapOn: "メールアドレス"
- inputText: ${email}
- tapOn: "パスワード"
- inputText: ${password}
- tapOn: "return"
- tapOn:
id: "loginButton"
- runFlow:
when:
visible: "パスワードを保存"
commands:
- tapOn: "パスワードを保存"
- assertVisible: "${email}さんこんにちは"
- assertVisible: "ログアウト"
(Maestro公式では、推奨されていません。)
CI/CDへの組み込み
MaestroではMaestro cloudというcloudのプラットフォームも用意されており、GitHub Actions・GitLab CI/CD・CircleCIなどの主要なCI/CDツールと連携することが出来ます。
ただし、料金は
Monthly Cost = (# of Uploads per Month) x (# of Flows) x ($0.10 USD)
という従量課金制のため、PRのたびにe2eテストを実施するといった使い方だと、結構な費用になりそうなのが残念・・・。
詳しくは公式サイトをご確認ください。 https://console.mobile.dev/pricing-calculator
まとめ
今回紹介出来ていませんが他にも、Maestro StudioというGUIツールを利用すると画面をポチポチするだけで欲しいテストケースのyaml出力も可能です。 セットアップの導入コストも低く、e2eテスト中の動画を簡単に保存できたりと非常に便利なテストツールなので導入しない手はないかと。
Masayuki Yamaji
Fusicの一番年老いた山路です。
Related Posts
Daiki Urata
2024/08/02
Daiki Urata
2024/06/10