モバイルアプリにテストを導入する前に
キッチハイク エンジニアの薬師寺です。 キッチハイクではReact Nativeアプリにテストを導入するにあたり、 テストライブラリの選定と、テストでは何を意識すればいいのかということを考えてきました。 今回の記事では、モバイルアプリのテストで考慮すべき点と、テストライブラリを比較して得た情報を紹介します。
- モバイルアプリにテストを導入する前に
- ソフトウェアのテストピラミッド
- モバイルアプリのテストピラミッドを築くのは難しい
- テスト導入のため、テストフレームワークの比較
- まとめ
- We’re Hiring!
ソフトウェアのテストピラミッド
Mike Cohnが著書『Succeeding with Agile: Software Development Using Scrum』で提唱した、 ソフトウェアのテストピラミッドをご存知の方は多いのではないでしょうか。
この図は、テストの種類と特徴、適切な比率を表した図です。
一般的に、E2Eテストはユーザの動作を予想してシナリオを書いて実行することが多いため、ひとつのテストあたりのカバー範囲は広くなり、再現度の高いテストができます。ただ依存する要素が多いのでそのぶん変更に弱く、実行時間も遅くなるのでコストがかかってしまいます。 また、カバー範囲が広いためテストが失敗したときに原因が単体テストに比べてわかりにくいこともあります。
逆に、単体テストはひとつの関数に絞ってテストをするため、変更に強く実行時間は高速のため、コストは抑えられます。代わりに、ひとつのテストあたりのカバー範囲は狭く、外部の関数をモックしてテストを書くことが多いため実際の挙動が再現できていない場合があることも考慮にいれなければなりません。
真ん中の統合テストは、単体テストとE2Eテストの中間ということで今回は省略します。
表にすると以下のようになります。
テストの種類 | テスト対象 | 実行速度 | メンテナンス性 | カバー範囲 | 再現度 |
---|---|---|---|---|---|
単体テスト | 1つのロジック | 速い | 高い | 狭い | 低い |
E2Eテスト | 複数のロジック、シナリオ | 遅い | 低い | 広い | 高い |
Androidのテスト基礎のページによると、単体テスト・統合テスト・E2Eテストの比率は7:2:1が理想とされています。
Fundamentals of Testing | Android Developers
また、ソフトウェアテスト自動化のアンチパターンとして、テストアイスクリームコーンという概念があります。
単体テストとE2Eテストの配分が逆転してしまっている状態のことで、 すぐにテストが壊れてしまってコストがかさんでしまうというデメリットがあります。
モバイルアプリのテストピラミッドを築くのは難しい
モバイルアプリのテストを完全に自動化するには以下のような障壁があります。
シミュレータと実機での環境差分
シミュレータと実機の間でも、挙動が違ってしまう箇所があります。 特にAndroidでは機種の数が多いため、どうしても差分が生まれてしまいます。
アニメーション・インタラクションなどのUI部分の差分
アニメーションやインタラクションの細かな挙動や変更点は、テストでは検知しづらく目視確認に頼る部分も多いです。
ネイティブ層の一部のテストが困難な箇所
プッシュ通知のテストなどは、ネイティブ層のAPIをテスト側から叩くのは難しいため、手動でテストをする必要があります。
そのため、テストが充実してきても手動テストが必要な部分は残ってしまいます。 カバーしたい不具合と、検知コストのトレードオフを考慮する必要があります。
テスト導入のため、テストフレームワークの比較
React Nativeアプリにテストを導入するにあたり、いくつかのライブラリを調査しました。
テストランナーとE2Eテストフレームワークの関係図
基本的に単体テストのスクリプトはどのテストランナーでも動きますが、UIテストのためのE2Eテストフレームワークはテストランナーとの組み合わせが必要です。 さらに、プラットフォームごとのUIテストフレームワークを経由して、各端末/シミュレータでテストを実行します。
比較表
ツール | 種類 | スター数 | 特徴 |
---|---|---|---|
Jest |
テストランナー | 25,744 | 多機能、RNのデフォルト |
Mocha |
テストランナー | 17,899 | シンプル、プラガブル |
Ava |
テストランナー | 16,333 | 最低限の機能、高速、効率的実行 |
Appium |
E2Eテストライブラリ | 9,400 | Seleniumを使用、ビルド不要、テストコードの言語が自由 |
Detox |
E2Eテストライブラリ | 4,675 | グレーボックス、テスト実行が非同期 |
Cavy |
E2Eテストライブラリ | 991 | React Native専用、高速 |
※ 記事執筆時(2019年5月31日)
テストランナーを選ぶ
各テストランナーの特徴を簡単に書いていきます。
jest
Facebookが作っているテストランナーで、React Nativeのデフォルトのテストランナーです。 zero config(設定が不要)をうたっています。 Jasmineというテストライブラリをラップしています。 デフォルトでコードカバレッジ解析・モック/スタブ・スナップショットテストが可能というとても多機能なテストランナーです。 また並列実行も可能なので、速度も担保されています。
mocha
mochaの特徴は、独立したオープンソースのテストランナーであることです。 また、npmのパッケージで最も他のパッケージから依存されているパッケージです。 シンプルで拡張性があり、別途拡張ライブラリの追加が必要ですが柔軟に機能を追加することができます。
AVA
機能を絞ってシンプルさと高速さを売りにした、最近注目されているテストランナーです。 全てのテストを非同期で並列に実施するという特徴があり、高速かつ効率的にテストを実行できるという強みがあります。
E2Eテストのライブラリを選ぶ
続いて、E2Eテストを実行するためのライブラリを紹介します。
Appium
AppiumはiOS, Android, Windowsアプリのテスト自動化のためのOSSライブラリです。 Seleniumを使用しているため、技術がある程度枯れており、WebアプリケーションのE2Eテストの知見を流用できるという特徴があります。
テスト用のアプリのビルドが不要で、本番環境と同じ環境でテストを実行できること、テストランナーとテストコードを記述する言語を自由に選択できるのが強みです。 AppiumのGithubには、JavascriptをはじめとしてJava, C#, PHP, Ruby, Pythonのサンプルコードが記載されています。
Detox
DetoxはWixが作成したテスト自動化フレームワークのOSSです。 Detoxは既存のE2Eテストフレームワークが抱える以下の2つの問題に立ち向かっています。
ブラックボックステスト
多くのE2Eテストはブラックボックステストを行うため、入出力 そのため、内部処理の動作保証がなされない、エラーになった箇所が特定しづらいという欠点がありました。 Detoxはグレーボックステストを採用して、この問題に対処しています。
Flakiness
Flakiness
とは、E2Eテストが速度などの問題で通るはずのテストに落ちてしまうなど、不安定な挙動をすることです。
既存のE2Eテストフレームワークでは内部で何回もsleep
を呼ぶなどのワークアラウンドが必要でした。
では、DetoxはどのようにFlakinessに対応しているのでしょうか?
Detoxでは、内部でEarlgrey
というGoogleが開発したiOS用の自動UIフレームワークを使用しています。
図のようにEarlgrey
がアプリ内の非同期処理を内側から待機して、テストとアプリの処理を同期させているのです。
なお、Android側はEspresso
というGoogle製のAndroid用テスト自動化フレームワークを使用しています。
Detoxの注意点としては一部iOSとAndroidで使える機能に差分があります。 Androidでは実機によるテスト実行が可能ですが、iOSはまだ対応していません。 また、通常のiOSアプリでもDetoxは使用可能ですが、Androidアプリは未対応です。
Cavy
CavyはReact Native専用のE2Eテストフレームワークです。
React Nativeのref
を利用するため、余計なオーバーヘッドがなく高速な動作が特徴です。
テストランナーはJestに依存しているためJestで実行する必要があります。
まとめ
この記事ではモバイルアプリのテストの概要と、有名なテストランナー、E2Eテストフレームワークを紹介しました。 モバイルアプリのテストの世界はまだ情報も少なく、ベストプラクティスが定まっていない印象があります。
一方で、テストピラミッドのような10年前から変わらない概念もあるため、最新の動向をウォッチしつつ、開発チームの状況に合わせて柔軟に対応できるように心がけています。
今後、キッチハイクではE2Eテストの知見を蓄積し、記事として公開していきたいと思います。
We’re Hiring!
キッチハイクでは私と一緒にテストと真正面から向き合ってくれるエンジニアを募集しています。一緒に楽しくテストとコードを書きましょう!