UI オートメーションによる iOS テスト入門
iOS アプリケーションと自動的に対話するスクリプトを書き、その結果を検証できることを想像してみてください。 UI オートメーションを使用すると、それが可能になります。 UI オートメーションは、XCTest では実現できない、iOS アプリケーションのより高いレベルのテストを実行するために Apple が提供するツールです。 ホワイト ボックス テストとブラック ボックス テストの比較
ソフトウェアの一部をテストする方法に関して、ホワイト ボックス テストとブラック ボックス テストの比較を聞いたことがあるかもしれません。 これらの概念に馴染みがない場合、それらがどのように機能するかを説明します。
ホワイトボックステスト
箱の中で動作しているソフトウェアがあると想像してください。 ホワイト ボックス テストでは、箱の中を見ることができ、ソフトウェアがどのように動作するかのすべての詳細な部分を見ることができます。 また、あなたが書いたテストからソフトウェアに深いレベルのフックを持つことができます。
ユニットテストはホワイトボックステストです。 ユニット テストを書くとき、テスト者はテスト対象のコードにきめ細かくアクセスすることができます。 テスト者は実際に、テスト対象のソフトウェアをメソッドまたはユニット レベルで活用するテストを書くことができます。
iOS ソフトウェア開発では、このタイプのテストを実行するために XCTest フレームワークを使用します。 XCTest を始めるにあたって私が書いた別のチュートリアルをご覧ください。
ブラック ボックス テスト
ブラック ボックス テストでは、箱が不透明です。 テスト者はボックスの中を見ることができません。 テスト者はテストを書くためにコードベースの実装にアクセスすることはできませんし、知ることもできません。
このタイプのテストを実行するには、少なくとも 2 つの方法があります。
- 定義済みのいくつかのステップを繰り返し手動で実行し、結果を視覚的に検証するテスト担当者です。
- 人間が操作する方法に似た動作をするAPIを使ってアプリケーションをテストするために、専用のツールを使用する。
iOSアプリケーション開発では、Appleはブラックボックステストを行うためにUIオートメーションというツールを提供しています。
2. UI Automationとは何ですか?
UI Automation は、iOS アプリケーションのより高度な自動テストのために、Apple が提供し、維持しているツールです。 テストは、Apple が定義した API に従って JavaScript で記述されます。
アプリケーション内のユーザー インターフェイス要素のアクセシビリティ ラベルに依存することにより、テストを簡単に書くことができます。 しかし、これらが定義されていない場合、利用可能な代替手段がありますのでご安心ください。
UI Automation API には、テストを記述するための典型的な xUnit ベースのフォーマットがありません。 ユニット テストとの違いの 1 つは、テスターが成功と失敗を手動で記録する必要があることです。 UI オートメーション テストは、Apple の開発者ツールに付属する Instruments ツール内の Automation インストゥルメントから実行されます。 テストは iOS シミュレーターまたは物理デバイスで実行できます。
3. UI オートメーション テストを書く
Step 1: サンプル プロジェクトを開く
iOS テストに関する前回のチュートリアルで使用したサンプル プロジェクトに、UI オートメーション テストを追加するための便利なフックを提供するいくつかの追加のユーザー インターフェイス要素を追加し、更新しました。 GitHub からプロジェクトをダウンロードしてください。 プロジェクトを開き、アプリケーションを実行して、すべてが期待どおりに動作していることを確認します。
テストを書く前に、サンプル アプリケーションを自由に試して、その機能に慣れ親しんでください。 ユーザーとして、テキスト フィールドにテキストを入力し、ボタンをタップすると、画面上に反転して入力された文字列を表示するラベルが表示されます。
Step 2: UI オートメーション テストの作成
サンプル アプリケーションに慣れたところで、次は UI オートメーション テストを追加しましょう。 UI オートメーションは、Instruments で見つけることができるツールです。 サンプルアプリケーションをInstrumentsで実行するには、XcodeのメニューからProduct > Profileを選択します。 ツールのリストから [Automation] を選択します。
メイン Instruments ウィンドウが開き、実行可能な 1 つのツールである Automation インストルメントが表示されます (Automation インストルメントは UI Automation テスト ケースを実行します)。 また、ウィンドウの下半分に、テキスト エディタのような領域が表示されます。 これがスクリプトエディタです。 ここにUIオートメーションテストを記述します。
テキスト フィールドへの参照を変数に格納することから始めます。
inputField.setValue("hi");
値が正常に設定されたことを確認し、設定されていれば、テストに合格します。
このテストはかなり些細なものですが、価値があります。 私たちは、アプリケーションが起動したときにテキスト フィールドがあるかどうかをテストし、テキスト フィールドの値としてランダムな文字列を設定できるかどうかをテストするテストを書いただけです。 もし信じないのであれば、ストーリーボードからテキストフィールドを削除してテストを実行してみてください。
このテストは、UI オートメーション テストを書くための 3 つの重要な要素を示しています。 最初に、それは単純なユーザー インターフェイス要素であるテキスト フィールドにアクセスする方法を示しています。 具体的には、target.frontMostApp().mainWindow().textFields()
を介してアプリケーションのベース ビュー上のすべてのテキスト フィールドの辞書にアクセスし、次に、キー Input Field
を持つものを探すことによって、関心のあるテキスト フィールドを見つけます。 このキーは、実際にはテキストフィールドのアクセシビリティラベルです。 この場合、ストーリーボードで定義されています。 また、NSObject
.
.
の accessibilityLabel
プロパティを使用して、コード内でアクセシビリティ ラベルを設定することもできます。アプリケーションのメイン ウィンドウ、最前面のアプリケーション、およびターゲットへのアクセスは、UI オートメーションで作業する場合によくあることです。 このチュートリアルの後半で、これをより簡単に、より冗長にしない方法を紹介します。
次に、これは、画面上のユーザー インターフェイス要素と対話できることを示しています。 この場合、テキスト フィールドの値を設定し、テキスト フィールドにテキストを入力してアプリケーションと対話するユーザーを模倣します。
そして 3 番目に、この例ではアプリケーションで起こることを検証するテクニックも示しています。 値が正常に設定された場合、テストは合格します。
ステップ 3: テストの保存
スクリプト エディタでテストを書くことは便利ですが、すぐに面倒になり、維持するのが難しくなります。 Instruments を終了すると、保存されていない変更はすべて破棄されます。 書いたテストを保存する必要があります。 テストをコピーして、お気に入りのテキスト エディタの新しいドキュメントに貼り付け、保存するだけです。 このチュートリアルで作成したテストは、サンプル プロジェクトの Jumblify/JumblifyTests/AutomationTests.js.
テストを実行するには、右側のペインでスクリプト エディタの隣にある中央タブを選択し、Add > Import.
Importするスクリプトを選ぶようプロンプトが出されます。 保存されたスクリプトに移動し、インポートします。 スクリプトエディターでスクリプトを変更することができます。 変更はすべて、作成した外部ファイルに自動的に保存されます。
Step 4: ボタンをタップする
ボタンとの対話をテストするために、テストを更新しましょう。 私たちのテストはすでにテキスト フィールドにテキストを追加しているので、ボタンをタップするコードだけを追加する必要があります。 最初に、ビューでボタンを見つけて、タップできるようにする方法を考えてみましょう。 これを達成するために少なくとも 3 つの方法があり、各アプローチにはトレードオフがあります。
Approach 1
私たちはプログラムによってスクリーン上の (X, Y) 座標をタップすることができます。 次のコード行でこれを行います。
target.tap({x: 8.00, y: 50.00});
もちろん、それが画面上のボタンの座標であるかどうかさえわかりませんし、このアプローチはこの仕事に適したツールではないため、それを気にするつもりはありません。 この方法はこの仕事に適したツールではないので、私はその存在を知ってもらうために言及しただけです。 target
の tap
メソッドを使用してボタンをタップすると、そのボタンが常にその特定の座標にあるとは限らないので、エラーが起こりがちです。
Approach 2
最初のテストでテキスト フィールドにアクセスした方法と同様に、メイン ウィンドウのボタンの配列を検索してボタンを見つけることも可能です。 キーを使用してボタンに直接アクセスする代わりに、メイン ウィンドウのボタンの配列を取得し、ボタンへの参照を取得するために配列インデックスをハードコードできます。 座標をハードコードしていませんが、ボタンを見つけるための配列インデックスをハードコードしています。 ページ上に別のボタンを追加すると、このテストが誤って壊れる可能性があります。
Approach 3
ここで、ページ上のボタンを見つける 3 つ目の方法として、アクセシビリティ ラベルを使用する方法を紹介します。 アクセシビリティ ラベルを使用すると、キーを使用して辞書内のオブジェクトを検索するように、ボタンに直接アクセスできます。
target.frontMostApp().mainWindow().buttons().tap();
ただし、上記の行をスクリプトに追加して実行すると、エラーが発生します。 これを行うには、Xcode に移動して、プロジェクトのストーリーボードを開きます。 ビューでボタンを見つけ、右側の Identity Inspector を開きます (ビュー > ユーティリティ > Identity Inspector)。 Accessibility が有効になっていることを確認し、ボタンの Label を Jumblify Button に設定します。
テストを再度実行するには、Xcode から Product > Run を選択してアプリケーションを実行し、Product > Profile を選択して再度アプリケーションをプロファイル化する必要があります。 これでテストが実行され、各テストはこれで合格するはずです。
Step 5: ごちゃごちゃした文字列の検証
先に述べたように、このアプリケーションは入力として文字列を受け取り、ユーザーがボタンをタップすると、反転した文字列が表示されます。 入力文字列が正しく反転されていることを確認するために、もう1つテストを追加する必要があります。 UILabel
に正しい文字列が入力されていることを確認するには、UILabel
を参照して表示される文字列を確認する方法を考え出す必要があります。 これは、自動テストを書くときによくある問題です。つまり、アプリケーションの要素を参照して、その要素に対してアサーションを行う方法を見つけ出すことです。
UI Automation API のほぼすべてのオブジェクトに logElementTree
というメソッドがあります。 このメソッドは、指定された要素のネストされた要素をログに記録します。 これは、アプリケーション内の要素の階層を理解するのに非常に役立ち、特定の要素をターゲットにする方法を見つけ出すのに役立ちます。
メイン ウィンドウの要素ツリーを記録することで、これがどのように機能するかを見てみましょう。 次のコードの行を見てください。
target.frontMostApp().mainWindow().logElementTree();
この行をテスト スクリプトに追加すると、次の出力になります:
ご覧のように、UIAWindow
のサブ要素 UIAStaticText
があり、それが ih
という名前を持ち、それはたまたま検証すべき逆文字列になることもわかります。
なぜ UIAStaticText
要素が存在するかどうかだけを確認する必要があるのでしょうか。 この要素の名前は入力文字列を反転したものであるため、この要素の存在を確認することで、文字列が正しく反転されたことが確認されます。 もし要素の名前-反転した文字列-によって参照されたときに要素が存在しなければ、それは文字列が正しく反転されていなかったことを意味します。 表面を擦る
アプリを使用している間、エンド ユーザーが iOS デバイスと対話できる他の方法が非常に多くあります。 これは、これらの相互作用をシミュレートするために UI オートメーションを使用できる他の多くの方法があることを意味します。 これらのインタラクションの包括的なリストを作成するのではなく、UI オートメーション リファレンス ドキュメントを参照してください。 いくつかのメソッドはオブジェクトに関する属性を取得するためのもので、他のメソッドは UIAWindow
の flickInsideWithOptions
のようにタッチ操作をシミュレートするものです。
Recording a Session
UI Automation でより複雑なアプリをテストしようとすると、探している要素を見つけるために logElementTree
を繰り返し使うことが非常に面倒な場合があるとわかります。 また、ビュー階層やナビゲーションが複雑なアプリケーションでは、これも面倒で複雑になります。 このような場合、Instrumentsの別の機能を使用して、一連のユーザー インタラクションを記録することができます。 さらに素晴らしいのは、記録されたインタラクションを再現するのに必要なUIオートメーションJavaScriptコードがInstrumentsにより生成されることです。
Instruments でオートメーション機器を選択し、ウィンドウの下部にある記録ボタンを探します。
記録ボタンをクリックすると、下のスクリーンショットに示すように Instruments が記録セッションを開始します。 Instruments は、あなたのインタラクションを基にリアルタイムでスクリプトを生成します。 試してみてください。 iOS Simulatorを回転させたり、ランダムな位置でタップしたり、スワイプジェスチャーを実行したり…。 UI オートメーションの可能性を探るのに役立つ本当に便利な方法です。
Avoiding a Monolithic Code Base
おそらく予見できると思いますが、同じ方法で作成したテスト ファイルにさらにテストを追加し続けていると、すぐに保守が困難になります。 そうならないためにはどうしたらいいでしょうか。 私のテストでは、この問題を解決するために2つのことを行っています:
- 1つの関数に対して1つのテストを行う。 これは、私たちが書くテストは、機能の特定の部分に焦点を当てる必要があることを意味します。
testEmptyInputField
. - 関連するテストを1つのファイルにまとめる、といった適切な名前もつけます。 私はまた、同じファイル内の関連するテストをグループ化します。 これにより、1つのファイル内のコードを管理しやすくすることができます。 これはまた、特定のファイルでテストを実行することによって、機能の別々の部分をテストすることを容易にします。
#import "OtherTests.js"
結論
このチュートリアルでは、より高度なテストの価値と、UI オートメーションがそのギャップを埋めるのに役立つことを学びました。 これは、信頼性の高い堅牢なアプリケーションを出荷するのに役立つ、ツールボックスの別のツールです。