Connect(); 2016

Volume 31 Number 12

Connect(); モバイル テスト - Xamarin Test Cloud によるモバイル アプリの自動テストのスケーリング

Justin Raczak; 2016

近年、チームでソフトウェアをビルドして配信する方法が大きく変化しています。以前は、要件を集めるプロセスに時間をかければ、初回のリリースで完璧な製品を確実に配信できると考えられていましたが、現在では、高速学習と迅速なイテレーションを組み合わせることが、成功への鍵であると考えられています。考え方が変われば、当然ワークフローも変わります。長期にわたるウォーターフォール モデルの品質保証フェーズが後に控え、数か月から数年に及ぶ開発サイクルは、高速学習には向いていません。フィードバックのサイクルを短くして、小さな変更を迅速に実装してユーザーにリリースする必要があります。迅速に展開するには、ソフトウェアが常に良好な状態にあることを把握しておく必要があります。これを実現するのがテストの自動化です。

自動テストがあれば、以前なら数日または数週間かかっていた方法でアプリをテストできます。数百行の新規コードで構成されるスプリントの完成を待たずに、コミットごとに追加される小さな変更をテストできます。自動テストを導入し、この連続式のテスト方法によって不具合を検出することで、デバッグに必要な時間を短縮できます。また、アプリの動作が継続的に検証されるため、準備ができ次第いつでもユーザーに配置できるという確信が得られます。自動テストは不具合を検出して、その日のうちに修正をリリースするという 1 つの世界観を実現します。しかし、モバイル デバイスや OS のメーカーが複雑な状勢にあるモバイル エコシステムは 1 つの課題を提示します。

Xamarin Test Cloud は、既存のワークフローに最低限の変更を加えるだけで、自動テストのスケール変換を迅速かつシンプルにします。Test Cloud は 400 を超える固有のデバイス構成を提供するため、独自のデバイス研究チームを編成して管理する経費や管理費を負担しないで、ユーザーにとって重要なデバイス モデルや OS バージョンでのアプリの動作を検証できます。多くの場合、コードにはほとんど、あるいはまったく変更を加えずに、この大きなメリットを活用できます。

Test Cloud は C# (UITest)、Ruby (Calabash)、および Java (Appium および Espresso) によるテストの作成をサポートします。今回のプロジェクトの変更に関する箇所では、最も要請の多い追加のテスト フレームワークである Appium with JUnit に着目し、Test Cloud で既存のテストを実行する際にプロジェクトに対して行う必要のある変更について取り上げます。また、テスト結果を確認して不合格になったテストをトラブルシューティングするための、Web インターフェイスについても見ていきます。必要となる個々の変更は、時間が経てば変わる可能性があります。これらの手順の最新バージョンは、bit.ly/2dhp2VQ (英語) で確認できます。

今回の例では、次の前提条件を想定します。

  • アクティブな Test Cloud アカウント (bit.ly/2e3YgTy (英語) でサインアップ)
  • インストール済みのコマンドライン ツール (手順は bit.ly/2dcrbXS (英語) 参照)
  • Android のネイティブ アプリケーション プロジェクト
  • Appium 1.5 準拠の Java with JUnit (バージョン 4.9 以上) で記述した既存の Appium テスト スイート
  • Maven ビルド システム (バージョン 3.3.9 以上)

ビルド システムへの変更

Test Cloud を使い始める前に、依存関係を追加して、必須ファイルを準備するタスクがお使いのビルドで利用できることを確認しておく必要があります。

Test Cloud の依存関係の追加: プロジェクトに Test Cloud を組み込んで、コンパイル時に Android と iOS の拡張ドライバーを利用できるようにするため、pom.xml ファイルに以下の依存関係を追加します。

<dependency>
  <groupId>com.xamarin.testcloud</groupId>
  <artifactId>appium</artifactId>
  <version>1.0</version>
</dependency>

アップロード プロファイルの追加: pom.xml の <profiles> タグ内に図 1 のプロファイルを追加します。<profiles> セクションがなければ、セクションを追加してからプロファイルを追加します。このプロファイルは、target/upload フォルダーにテスト クラスとすべての依存関係を含めます。このフォルダーに含めることで、後で Test Cloud にアップロードできます。

図 1 Test Cloud のアップロード プロファイル

<profile>
  <id>prepare-for-upload</id>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.10</version>
        <executions>
          <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>${project.build.directory}
                /upload/dependency-jars/</outputDirectory>
              <useRepositoryLayout>true</useRepositoryLayout>
              <copyPom>true</copyPom>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <executions>
          <execution>
            <id>copy-pom-file</id>
            <phase>package</phase>
            <goals>
              <goal>testResources</goal>
            </goals>
            <configuration>
              <outputDirectory>${project.build.directory}
                /upload/</outputDirectory>
              <resources>
                <resource>
                  <directory>
                    ${project.basedir}
                  </directory>
                  <includes>
                    <include>pom.xml</include>
                  </includes>
                </resource>
              </resources>
            </configuration>
          </execution>
          <execution>
            <id>copy-testclasses</id>
            <phase>package</phase>
            <goals>
              <goal>testResources</goal>
            </goals>
            <configuration>
              <outputDirectory>${project.build.directory}
                /upload/test-classes</outputDirectory>
              <resources>
                <resource>
                  <directory>
                    ${project.build.testOutputDirectory}
                  </directory>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</profile>

テストへの変更

ビルドを構成したら、Test Cloud の Java 拡張機能を使用するように、テスト クラスを変更する必要があります。

テスト クラスへのインポートの追加: テスト クラスに以下のパッケージをインポートします。

import com.xamarin.testcloud.appium.Factory;
import com.xamarin.testcloud.appium.EnhancedAndroidDriver;
import org.junit.rules.TestWatcher;
import org.junit.Rule;

TestWatcher のインスタンス作成: テスト クラスの 1 つに以下のインスタンス作成を挿入します。

@Rule
public TestWatcher watcher = Factory.createWatcher();

ドライバー宣言の更新: AndroidDriver<MobileElement> 宣言をすべて以下のように EnhancedAndroidDriver­<MobileElement> に置き換えます。

private static EnhancedAndroidDriver<MobileElement> driver;

ドライバーのインスタンス作成の更新: ドライバーのインスタンスを作成する以下の形式の行を、

Driver = new AndroidDriver<MobileElement>(url, capabilities);

以下のように置き換えます。

Driver = new EnhancedAndroidDriver<MobileElement>(url, capabilities);

拡張ドライバーにより、driver.label(“myTestStepLabel”) を使用して、テストの手順に「ラベル」を付けることができるようになります。この手法では、テスト手順のラベルと付属のスクリーンショットを生成し、Test Cloud のテスト レポートに表示できるようにします。@After 方式でラベルを呼び出し、テストが完了する前に、最終状態のアプリのスクリーンショットを取得することをお勧めします。テストが不合格になった場合にもスクリーンショットを取得しておくと、不合格の原因について有意義な洞察が得られることがあります。実際には、次のようになります。

@After
  public void tearDown(){
    driver.label("Stopping app");
    driver.quit();
  }

Test Cloud へのアップロード

これでプロジェクトのすべての前提条件が整ったので、ファイルを準備して Test Cloud で実行できるようになります。アップロードの手順に進む前に、ローカルでの実行を試して、すべてが想定どおりに動作することを確認します。先ほど行ったいずれかの構成変更をトラブルシューティングする必要がある場合は、ローカルの方がはるかに手早く実行できます。

テスト クラスとすべての依存関係を target/upload フォルダーに含めるために、以下のコマンドを実行します。

mvn –DskipTests -P prepare-for-upload package

確実にアップロードできるように、target/upload ディレクトリがプロジェクトの root フォルダー内に存在することを確認しておきます。これが Test Cloud 内で新規アプリになる場合は、テスト実行の一環としてアプリを作成する必要があります。フローに従い、新しいテスト実行を作成してデバイスを選択し、設定を行って、実行する必要があるコマンドを生成します。この演習には、結果を手早く確認できるように、Tier 1 カテゴリから少数のデバイスを選択することをお勧めします。生成されたコマンドをコピーして、コマンド ラインで実行します。

ファイル アップロードのネゴシエートと検証に成功すると、デバイスがプロビジョニングされ、アプリがインストールされて、テストが実行されます。Test Cloud の運用モデルは、デバイスの同時実行を基本にするか、平行して使用できる物理デバイス数を基本にします。たとえば、Nexus 5X、Nexus 6P、Samsung Galaxy S5、Samsung Galaxy S6、HTC M8 という 5 つのデバイスを所有するユーザーには、この 5 つで同時にアプリをテストします。この効率性が Test Cloud の最大のメリットの 1 つで、追加の待ち時間がほとんど発生しないで、さらに多くのデバイスにテスト範囲を簡単に広げることができます。

コマンドライン ツールは、テスト実行の状態に応じて更新をストリーミングし、実行が終了するとテスト レポートへのリンクを提示します。提示されたテスト レポートのリンクをたどって、結果を検証します。

結果は、次の 3 つの詳細レベルで表示できます。

  • Overview report (概要レポート)
  • Device grid (デバイス グリッド)
  • Device detail report (デバイス詳細レポート)

それぞれを順番に確認していきます。

概要レポート: 概要レポートは、OS バージョン、製造元、フォーム ファクターごとの合否の詳細、不合格の状態、および対象のデバイス設定や合計実行時間などの実行自体に関する詳細を含め、テスト実行の概要情報を提供します (図 2 参照)。

概要レポート
図 2 概要レポート

テスト実行で不合格になった場合、より詳しく確認して根本的な原因を究明し、デバッグ用のデータを収集したいと考えます。次の詳細レベルはデバイス グリッドです。

デバイス グリッド: デバイス グリッドは、各手順で取得したスクリーンショットに沿って 1 行ごとにテスト結果を移動する効率的な機能を提供します。テスト手順に不具合が明示されている場合、不具合のある手順にすぐに移動して、各デバイスでアプリの表示状態を検証できます。デバイスの広範囲のセットをテストする場合は、不合格になったデバイスのみに絞って表示し、より整理された画面を検証用に作り出すことができます。不具合の原因がこの詳細レベルでは明示されない場合、さらに詳しいレベルに掘り下げて、デバイスの詳細を表示します (図 3 参照)。

デバイス グリッド レポート
図 3 デバイス グリッド レポート

デバイス詳細レポート: デバイス詳細ビューでは、同じテスト手順へのアクセス方法とスクリーンショットを提供しますが、CPU やメモリ使用率など、選択したデバイス固有の追加詳細を提示します。このビューから、テストの不合格を調査する際に最も役立つと考えられるアーティファクトである、デバイス ログやスタック トレースにアクセスすることもできます (図 4 参照)。

デバイス詳細レポート
図 4 デバイス詳細レポート

ここでは、Test Cloud で最も一般的な次のワークフローに従いました。

  1. テストを実行する (手動、または継続的インテグレーション (CI) を利用)。
  2. 結果を確認する。
  3. デバッグのアーティファクトを取得する。
  4. 修正する。

次に、パイプラインのスムーズな流れを保つために、対象とするデバイス戦略に関するシンプルな考え方や、パフォーマンスのためにテスト ワークフローを最適化する簡単な方法について少し確認しておきます。

デバイスの対象範囲の検討

組織がサポートするデバイスの選択と、それに対する最終的なテストの選択は、テスト自体と同じくらい重要です。この観点で考えを進めていくうえで役立つ集計データや汎用的な市場データの情報源はたくさんありますが、最も影響の大きい情報源はアプリ自体のユーザー ベースの使用率データです。もちろん、管理対象の既知のデバイス セットに社内配布されるアプリは例外です。私物デバイスの業務利用 (BYOD) ポリシーの下で配布される社外アプリ、一般消費者向けアプリ、社内アプリの場合は、使用率データが最善の情報源になります。

対象ユーザーが使用するデバイスに対する洞察を得る場合、市場に出回っている多数のツールが役立つこともあります。これらのツールのデータ セットから、サポートするデバイス一覧を予測できます。どのデバイスをサポートするかを集計一覧から判断する方法に取り決めはありません。多くの場合、すべてのデバイスの使用率データをサポートすることには意味がありません。すぐに手に負えなくなり、コストも膨らみます。ユーザー ベースの中で一定の比率を占めるデバイスをテスト範囲に決めるのが妥当です。あるいは、ユーザー数という観点から考え、ユーザー数が 500 未満のデバイスは対象範囲からはずして必要なデバイスをサポートするように決めることもできます。e コマース アプリを使用している場合は、最高速度と最高頻度の取引を示すデバイスが確実に対象範囲になるように、使用率データと取引データを相互参照します。この場合も、優れたデバイス サポート一覧にするために採用する具体的な方法は、ビジネス ニーズやビジネス目標に基づきます。

モバイル市場は急速に変化していることを忘れないでください。つまり、サポート一覧が正確かつ有意義なものになるように、使用率データを定期的に調査する必要があります。新しいデバイス モデルや OS の発表など、データを見直すタイミングを示す市場の兆候を見逃さないようにします。

テスト パイプラインの最適化

テストの自動化の最大のメリットを引き出す最善の方法は、早い段階で頻繁にテストを実施することです。これにより、バグ修正に関連する時間とコストを削減して、配置のパイプラインを整理しておくことができます。しかし、チームや処理の規模が大きくなると、パイプラインでの待機時間が増大し、開発者の生産性を低下させる可能性があります。そこで、パイプラインを整理して、高い生産性を保つ方法を確認しておきます。

すべてのテストは等価ではない: プロジェクトが時間の経過と共に成長すると、テスト スイートの実行に時間がかかるようになります。ある転換点を境に、簡単な変更を行った後のテスト スイートの実行が厄介かつ面倒になっていきます。こうなると、多くの場合、テストを完全に省略するなどの悪習慣を引き起こします。アプリケーションのクリティカル パスを早い段階で検討することで、このような状況を回避できます。つまり、アプリケーションのどのフローやエクスペリエンスが絶対に機能しなければならないかを検討します。 先ほどの e コマース アプリの例を取り上げると、この場合は、ユーザーが製品を参照し、製品をカートに追加して、清算できることが、クリティカル パスになるでしょう。ユーザーが通知設定を行えるという点は、あまり重要ではありません。このように固定のしくみがある場合は、プッシュごとや、場合によってはコミットごとにテストを実行する方がずっと現実的です。小さな変更に対してすべてのテスト スイートを実行するのではなく、クリティカル パスの部分に対応するテスト スイートのみを実行します。どの程度厳密にこの説明どおりに実施できるかは、使用するテスト フレームワーク次第です。

適時に適切なデバイスを: 品質の観点からは、1 つの機能ブランチに対してすべてのプッシュをテストするのが理想的かもしれません。しかし、これを実施すると、大規模チームの場合、特にさまざまなデバイス設定をサポートしているチームの場合は、すぐに高コストになります。このようなテスト実行では、対象とするデバイス対して段階的な戦略を適用することで、付加コストを削減できます。非運用ブランチのビルドは、サポートするすべてのデバイスでテストする必要がありません。 代わりに、より短い待機時間で効率的なテスト実施を維持できる妥当なデバイス数を選択します。CI の運用前のビルドの場合は、デバイス サポート一覧から最もよく使われているデバイス モデルと OS バージョンをサンプリングすると、ビルド時間を 1 時間以上増大させることなく、有意義なテスト範囲のレベルを提示できます。ローカル ワークステーションからテストする個人開発者の場合は、1 つまたは 2 つのデバイスに対してテストすれば十分でしょう。

ここでは、テスト実施のワークフローを構成する場合の考え方について、数例を取り上げたにすぎません。もっと広い観点としては、パイプライン フローが最適かどうかを時間をかけて問い直すことです。この問いに対して事前に答えが出ているとしても、実施するすべての作業がそうであるように、常に定期的に調査して実情に合わせるようにします。

まとめ

今回は、シミュレーターや単一のローカル デバイスでテストを実行する方法から、Xamarin Test Cloud を使用して何百ものデバイス設定を活用する方法へ、いかに簡単に移行できるかを確認しました。テスト実施のワークフローを整理する戦略や、テスト リソースから最大のメリットを引き出す戦略についても取り上げました。まだ Test Cloud を使用していない場合は、bit.ly/2e3YgTy (英語) で無償の評価版にサインアップすると、今日からでもプロジェクトで使い始めることができます。


Justin Raczak は、モバイル テスト自動化サービスを先導するマイクロソフトのプログラム マネージャーです。最近マイクロソフトに入社したばかりですが、ここ 3 年間、継続的なアプリ配信を推進する中で、テストの自動化とその役割に着目してきました。Justin Raczak の連絡先は justin.raczak@microsoft.com (英語のみ) です。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Simon Søndergaard に心より感謝いたします。
Simon Søndergaard はマイクロソフトのソフトウェア エンジニアです。