Top View


Author Masayuki Yamaji

MaestroならExpo(React Native)環境でもe2eテストが簡単

2023/10/30

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

Masayuki Yamaji

Fusicの一番年老いた山路です。