Xamarin.UITest

重要

Visual Studio App Center は、2025 年 3 月 31 日に廃止される予定です。 完全に廃止されるまで Visual Studio App Center を引き続き使用できますが、移行を検討できる推奨される代替手段がいくつかあります。

詳細については、サポートタイムラインと代替手段に関するページを参照してください。

Xamarin.UITest は、iOS および Android アプリの UI 受け入れテストに NUnit を使用する C# テスト フレームワークです。 Xamarin.iOS および Xamarin.Android プロジェクトと緊密に統合されていますが、ネイティブの iOS および Android プロジェクトでも使用できます。 Xamarin.UITest は、Android および iOS デバイスで NUnit テストを実行できるようにする オートメーション ライブラリ です。 テストでは、テキストの入力、ボタンのタップ、ジェスチャ (スワイプなど) と同様に、ユーザー インターフェイスと対話します。

通常、各 Xamarin.UITest は、 と呼ばれるメソッドとして記述されます [Test]。 テストを含む クラスは、 と呼ばれます [TestFixture]。 テストフィクスチャには、1 つのテストまたはテストグループが含まれています。 フィクスチャは、テストが完了したときに実行する必要があるテストの実行とクリーンアップを行うセットアップも担当します。 各テストは、 Arrange-Act-Assert パターンに 従う必要があります。

  1. 配置: テストは条件を設定し、テストを実行できるように初期化します。
  2. 動作: テストでは、アプリケーションと対話したり、テキストを入力したり、ボタンを押したりします。
  3. Assert: テストでは、Act ステップで実行されたアクションの結果を調べて、正確性を判断します。 たとえば、アプリケーションでは、特定のエラー メッセージが表示されていることを確認できます。

Xamarin.UITest の使用を開始するのに最適な時期は、モバイル アプリケーションの開発中です。 自動テストは、次の一覧で説明する手順に従って、機能の開発中として記述されています。

  1. Android または iOS アプリケーションで機能を開発します。
  2. テストを記述し、ローカルで実行して機能を確認します。
  3. App Center Test で新しいテスト実行を作成するか、既存のテスト実行を使用します。
  4. IPA または APK をコンパイルし、テストと共に App Center テストにアップロードします。
  5. App Center Test によって公開される問題やバグを修正します。
  6. アプリケーションの次の機能に進み、プロセスを繰り返します。

アクティブな開発中でなくなった既存のアプリケーションの場合、自動テストをさかのぼって追加してもコスト効率が高くない場合があります。 代わりに、バグを修正するときに Xamarin.UITest を使用することをお勧めします。 たとえば、自動テストを行っていないアプリケーションで、ユーザーがバグを報告しているとします。 そのバグを修正するために割り当てられた開発者は、次のアクションの一部 (またはすべて) を実行する可能性があります。

  • バグまたは回帰を手動で確認します。
  • バグを示す Xamarin.UITest を使用してテストを記述します。
  • App Center テストにテストを送信して、関連するデバイスに対するバグの範囲と影響に関する分析情報を取得します。
  • バグを修正します。
  • 渡された Xamarin.UITest でバグが修正されたことを証明します。
  • 修正プログラムを送信し、App Center テストにテストして、関連するデバイスでバグが修正されたことを確認します。
  • バージョン 管理にテストを渡すことを確認します。

自動化された UI テストは、画面上のビューの検索と操作に大きく依存します。 Xamarin.UITest は、相互に動作する 2 つの重要な API セットを使用して、この要件に対処します。

  1. ビューで実行できるアクション - Xamarin.UITest には、ビューのタップ、テキストの入力、ビューでのスワイプなどの一般的なユーザー アクションをテストでシミュレートできる API が用意されています。
  2. 画面上でビューを検索するためのクエリ - Xamarin.UITest フレームワークの一部は、画面上のビューを検索する API です。 クエリは、実行時にビューの属性を検査し、アクションが処理できるオブジェクトを返すことによってビューを検索します。 このような方法でのクエリは、画面のサイズ、向き、レイアウトに関係なく、ユーザー インターフェイスに対してテストを記述できる強力な手法です

テストの記述を支援するために、Xamarin.UITest には read-eval-print-loop (REPL) が用意されています。 REPL を使用すると、開発者とテスト担当者は、アプリケーションの実行中に画面を操作でき、クエリの作成が簡略化されます。

Xamarin.UITest API の概要

モバイル アプリケーションとのテスト操作はすべて、 の Xamarin.UITest.IAppインスタンスを介して行われます。 このインターフェイスは、テストがアプリケーションと共同作業し、ユーザー インターフェイスと対話するために重要なメソッドを定義します。 このインターフェイスには、次の 2 つの具体的な実装があります。

  • Xamarin.UITest.iOS.iOSApp このクラスは、iOS に対するテストを自動化します。
  • Xamarin.UITest.Android.AndroidApp このクラスは、Android でのテストを自動化するためのクラスです。

iOSApp オブジェクトと AndroidApp オブジェクトは直接インスタンス化されません。 代わりに、ヘルパー ConfigureApp クラスを使用して作成されます。 このクラスは、 または AndroidApp が適切にiOSAppインスタンス化されることを保証するビルダーです。

テストごとに新 IApp しいインスタンスを使用することをお勧めします。 新しいインスタンスを使用すると、あるテストが別のテストに波及するのを防ぐことができます。 NUnit テストで のインスタンス IAppを初期化できる場所は 2 つあります。

  • メソッドではSetUp、通常、テストフィクスチャは関連するテストの論理的なグループであり、それぞれが互いに独立して実行されます。 このシナリオでは、 メソッドで をIAppSetUp初期化し、各テストで新しい IApp を使用できるようにする必要があります。
  • TestFixtureSetupメソッドでは、状況によっては、1 つのテストで独自のテスト フィクスチャが必要になる場合があります。 この場合、 メソッドでオブジェクトを 1 回初期化する方が IApp 理に TestFixtureSetup かなっている場合があります。

構成が完了すると IApp 、テスト対象のアプリケーションとの対話が開始される場合があります。 そのためには、画面に表示されるビューへの参照を取得する必要があります。 Xamarin.UITest の多くのメソッドは、ビューを Func<AppQuery, AppQuery> 検索するためのパラメーターを受け取ります。 たとえば、次のスニペットは、ボタンをタップする方法を示しています。

app.Tap(c=>c.Button("ValidateButton"));

Xamarin.UITest フレームワーク内には IApp 、iOS 用と Android 用の 2 つの実装があります。

iOS アプリケーションの IApp を初期化する

Xamarin.UITest は、iOS でテストを実行すると、iOS シミュレーターのインスタンスを起動し、アプリケーションをデプロイして起動し、テストの実行を開始します。 iOS アプリケーションは既にビルドされている必要があります。 Xamarin.UITest は、アプリケーションをコンパイルしてアプリ バンドルを作成しません。

メソッドを AppBundle 使用して、アプリ バンドルが見つかるファイル システム上の場所を指定できます。 これを行うには、絶対パスまたは相対パスの 2 つの方法があります。 このスニペットは、アプリ バンドルへの絶対パスの使用を示しています。

IApp app = ConfigureApp
    .iOS
    .AppBundle("/path/to/iosapp.app")
    .StartApp();

部分的なパスは、Xamarin.UITest アセンブリに対する相対パスである必要があります。 このスニペットは次の例です。

IApp app = ConfigureApp
    .iOS
    .AppBundle("../../../iOSAppProject/bin/iPhoneSimulator/Debug/iosapp.app")
    .StartApp();

相対パスの例では AppBundle 、Xamarin.UITest アセンブリから 3 つのディレクトリを上に移動し、iOS アプリケーション プロジェクトのプロジェクト ツリーを下に移動してアプリ バンドルを見つけます。

ConfigureApp には、 の構成 IAppに役立つ他の方法があります。 詳細については、 iOSAppConfigurator クラスを参照してください。 さらに興味深い方法の一部を次の表に示します。

メソッド 説明
AppBundle このメソッドは、テスト時に使用するアプリ バンドルへのパスを指定します。
Debug このメソッドは、テスト ランナーでデバッグ ログ メッセージを有効にします。 この方法は、シミュレーターでのアプリケーションの実行に関する問題のトラブルシューティングに役立ちます。
DeviceIdentifier デバイス識別子と共に使用するようにデバイスを構成します。 この方法について、以下にさらに詳しく説明する。
EnableLocalScreenshots テストをローカルで実行するときにスクリーンショットを有効にします。 テストがクラウドで実行されている場合、スクリーンショットは常に有効になります。

特定の iOS シミュレーターで iOS テストを実行する方法の詳細については、「 iOS シミュレーターのデバイス ID を決定する」を参照してください。

Android アプリケーション用の IApp を初期化する

Xamarin.UITest は、接続されているデバイスまたは既に実行されている Android エミュレーターのインスタンスに既存の APK をデプロイします。 アプリが起動し、テストが実行されます。 Xamarin.UITest では、APK をビルドすることも、Android エミュレーターのインスタンスを開始することもできません。

の メソッドIAppApkFile、APK が見つかるファイル システム上の場所を指定するために使用されます。 これを行うには、絶対パスまたは相対パスの 2 つの方法があります。 このスニペットは、APK への絶対パスの使用を示しています。

IApp app = ConfigureApp
    .Android
    .ApkFile("/path/to/android.apk")
    .StartApp();

部分的なパスは、Xamarin.UITest アセンブリに対する相対パスである必要があります。 このスニペットは次の例です。

IApp app = ConfigureApp
    .Android
    .ApkFile("../../../AndroidProject/bin/Debug/android.apk")
    .StartApp();

相対パスの例では ApkFile 、Xamarin.UITest アセンブリから 3 つのディレクトリを上に移動し、Android アプリケーション プロジェクトのプロジェクト ツリーを下に移動して apk ファイルを見つけます。

複数のデバイスまたはエミュレーターが接続されている場合、Xamarin.UITest はテストの実行を停止し、テストの目的のターゲットを解決できないため、エラー メッセージを表示します。 この場合、テストを実行するには、デバイスまたはエミュレーターの シリアル ID を 指定する必要があります。 たとえば、(シリアル ID と共に adb devices ) コンピューターに接続されているすべてのデバイス (またはエミュレーター) を一覧表示する コマンドからの次の出力を考えてみましょう。

$ adb devices
List of devices attached
192.168.56.101:5555 device
03f80ddae07844d3    device

デバイスは、 メソッドを DeviceSerial 使用して指定できます。

IApp app = ConfigureApp.Android.ApkFile("/path/to/android.apk")
                               .DeviceSerial("03f80ddae07844d3")
                               .StartApp();

ユーザー インターフェイスの操作

ビューを操作するために、多くの IApp メソッドはビューを Func<AppQuery, AppQuery> 検索するためのデリゲートを受け取ります。 このデリゲートでは、Xamarin.UITest がビューを検索する方法の中核となる を使用 AppQuery します。

AppQuery は、ビューを検索するためのクエリを構築するための fluent インターフェイス です。 提供されるメソッド AppQuery のうち、 Marked メソッドは最も単純で柔軟性の高いメソッドの 1 つです。 このメソッドではヒューリスティックを使用してビューを検索し、次のセクションで詳しく説明します。 ここでは、アプリケーションと対話するための多くの方法があることを IApp 理解することが重要です。 これらのメソッドでは、 を Func<AppQuery, AppQuery> 使用して、操作するビューへの参照を取得します。 によって AppQuery 提供されるより興味深い方法の一部を次に示します。

メソッド 説明
Button 画面上で 1 つ以上のボタンを見つけます。
Class 指定したクラスのビューの検索を試みます。
Id 指定した ID を持つビューの検索を試みます。
Index . 一致するビューのコレクションから 1 つのビューを返します。 通常、他のメソッドと組み合わせて使用します。 0 から始まるインデックスを取得します。
Marked 以下で説明するヒューリスティックに従ってビューを返します。
Text 指定されたテキストを含むビューと一致します。
TextField Android EditText または iOS UITextFieldと一致します。

たとえば、次のメソッドは、"SaveUserdataButton" というボタンのタップをシミュレートする方法を示しています。

app.Tap(c=>c.Marked("SaveUserDataButton"));

は fluent インターフェイスであるため AppQuery 、複数のメソッド呼び出しを連結することができます。 ビューをタップする場合の、このより複雑な例を考えてみましょう。

app.Tap(c=>c.Marked("Pending")
            .Parent()
            .Class("AppointmentListCell").Index(0));

ここで、 は最初に AppQuery というマークの付いた Pendingビューを見つけ、そのビューの最初の親である型を AppointmentListCell 選択します。

モバイル アプリを見て、これらのクエリを作成しようとするのは難しい場合があります。 Xamarin.UITest には、画面のビュー階層の探索、クエリの作成の実験、アプリケーションとの対話に使用できる REPL が用意されています。

REPL の使用

REPL を開始する唯一の方法は、既存のテスト内で IApp.Repl メソッドを呼び出す方法です。 これには、メソッドで使用できる のIAppインスタンスを構成する NUnit TestFixtureを作成するTest必要があります。 次のコード スニペットは、その方法の例を示しています。

[TestFixture]
public class ValidateCreditCard
{
    IApp app;

    [SetUp]
    public void Setup()
    {
        app = ConfigureApp.Android.ApkFile("/path/to/application.apk").StartApp();
    }
    [Test]
    public void CreditCardNumber_TooLong_DisplayErrorMessage()
    {
        app.Repl();
    }
}

Visual Studio の余白を右クリックし、[実行] を選択してテストを実行するには、次の 手順を実行します。

テストの実行オプションを含むポップアップ メニューのスクリーンショット

テストが実行され、次の Repl スクリーンショットに示すように、メソッドが呼び出されると、Xamarin.UITest によってターミナル セッションで REPL が開始されます。

Xamarin.UITest REPL を実行している macOS ターミナルのスクリーンショット

REPL によって、 と呼ばれる appIAppインスタンスが初期化され、アプリケーションと対話します。 最初に行うことの 1 つは、ユーザー インターフェイスを調べる方法です。 REPL には、 tree これを行うコマンドがあります。 表示された画面にビューの階層が出力されます。 例として、アプリケーションの次のスクリーンショットを考えてみましょう。

iPhone で実行されているサンプル アプリケーションのスクリーンショット

コマンドを tree 使用して、この画面の次の階層を表示できます。

App has been initialized to the 'app' variable.
Exit REPL with ctrl-c or see help for more commands.

>>> tree
[UIWindow > UILayoutContainerView]
  [UINavigationTransitionView > ... > UIView]
    [UITextView] id: "CreditCardTextField"
      [_UITextContainerView]
    [UIButton] id: "ValidateButton"
      [UIButtonLabel] text: "Validate Credit Card"
    [UILabel] id: "ErrorrMessagesTestField"
  [UINavigationBar] id: "Credit Card Validation"
    [_UINavigationBarBackground]
      [_UIBackdropView > _UIBackdropEffectView]
      [UIImageView]
    [UINavigationItemView]
      [UILabel] text: "Credit Card Validation"
>>>

ValidateButton の をUIButton使用して、このビューidに が存在することがわかります。 コマンドによって表示される情報を使用して、ビューを tree 検索して操作するために必要なクエリを作成できます。 たとえば、次のコードはボタンのタップをシミュレートします。

app.Tap(c=>c.Marked("ValidateButton"))

コマンドを入力すると、REPL によってバッファーに記憶されます。 REPL は、 copy このバッファーの内容をクリップボードにコピーするコマンドを提供します。 これにより、テストのプロトタイプを作成できます。 REPL で実行した作業を を使用 copyしてクリップボードにコピーし、それらのコマンドを 内に [Test]貼り付けることができます。

マークを使用してビューを検索する

AppQuery.Marked メソッドは、画面上のビューを照会する便利で強力な方法です。 これは、画面上のビューのビュー階層を調べ、ビューのプロパティと指定された文字列を照合することで機能します。 Marked は、オペレーティング システムによって動作が異なります。

マークが付いた iOS ビューの検索

iOS ビューは、次のいずれかの属性を使用して配置されます。

  • AccessibilityIdentifierビューの
  • AccessibilityLabelビューの

例として、 を作成 UILabel して を設定する次の C# スニペットを AccessibilityLabel考えてみましょう。

UILabel errorMessagesTextField = new UILabel(new RectangleF(10, 210, 300, 40));
errorMessagesTextField.AccessibilityLabel = "ErrorMessagesTextField";
errorMessagesTextField.Text = String.Empty;

このビューは、次のクエリで確認できます。

AppResult[] results = app.Marked("ErrorMessagesTextField");

マークが付いた Android ビューの検索

Android ビューは、次のいずれかのプロパティに基づいて配置されます。

  • Idビューの
  • ContentDescriptionビューの
  • Textビューの

たとえば、次のボタンが定義されている Android レイアウトを考えてみましょう。

<Button
    android:text="Action 1"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/action1_button"
    android:layout_weight="1"
    android:layout_marginLeft="5dp" />

このボタンの がandroid:idaction1_buttonであり、 android:textアクション 1 であることがわかります。 次の 2 つのクエリのいずれかを実行すると、画面にボタンが表示されます。

  • app.Query(c=>c.Marked("action1_button"));
  • app.Query(c=>c.Marked("Action 1"));

Xamarin.UITest.IApp を使用したアプリケーションの制御

構成と初期化が完了すると IApp 、テストでアプリケーションとの対話が開始される可能性があります。 を使用 Func<AppQuery, AppQuery> するメソッドの 1 つの例として、 メソッドがあります IApp.Query() 。 このメソッドはクエリを実行し、結果を返します。 最も簡単な例を次のスニペットに示します。このスニペットは、画面に表示されるすべてのビューの一覧を返します。

AppResult[] results = app.Query(c=>c.All())

次の表は、 を使用して AppQuery 画面上のビューを検索する他のいくつかの例を示しています。

構文 結果
app.Query(c=>c.Class("UILabel")) メソッドは .Class() 、iOS UILabelのサブクラスであるビューのクエリを実行します。
app.Query(c=>c.Id("txtUserName")) メソッドは.Id()txtUserName の ビューに対してIdクエリを実行します。
app.Query(c=>c.Class("UILabel").Text("Hello, World")) "Hello, World" というテキストを持つすべての UILabel クラスを検索します。
results = app.Query(c=>c.Marked("ValidateButton")) 指定したテキストで マーク されているすべてのビューを返します。 メソッドは Marked 、クエリを簡略化できる便利なメソッドです。 次のセクションで説明します。

次の表は、画面上のビューを操作したり操作したりするために使用できる、 によって IApp 提供されるメソッドの一部 (すべてではありません) を示しています。

説明
PressEnter アプリで Enter キーを押します。
Tap 一致する要素のタップ/タッチ ジェスチャをシミュレートします。
EnterText ビューにテキストを入力します。 iOS アプリケーションでは、Xamarin.UITest はソフト キーボードを使用してテキストを入力します。 これに対し、Xamarin.UITest は Android キーボードを使用せず、ビューにテキストを直接入力します。
WaitForElement 画面にビューが表示されるまで、テストの実行を一時停止します。
Screenshot(String) 現在の状態のアプリケーションのスクリーンショットを取得し、ディスクに保存します。 取得したスクリーンショットに FileInfo 関する情報を含む オブジェクトが返されます。
Flash この方法では、選択したビューが画面上で "フラッシュ" または "ちらつき" になります。

インターフェイスのIApp詳細については、および iOSAppAPI ドキュメントIAppAndroidApp参照してください。

これらのメソッドの使用方法の例として、上に示したスクリーンショットの次のテストを検討してください。 このテストでは、クレジット カードの 17 桁の数値をテキスト フィールドに入力し、画面上のボタンをタップします。 その後、画面で、有効なクレジット カード番号には長すぎることをユーザーに通知するエラー メッセージが表示されます。

[Test]
public void CreditCardNumber_TooLong_DisplayErrorMessage()
{
    /* Arrange - set up our queries for the views */
    // Nothing to do here, app has been instantiated in the [SetUp] method.

    /* Act */
    app.EnterText(c => c.Marked("CreditCardTextField"), new string('9', 17));
    // Screenshot can be used to break this test up into "steps".
    // The screenshot can be inspected after the test run to verify
    // the visual correctness of the screen.
    app.Screenshot("Entering a 17 digit credit card number.");

    app.Tap(c => c.Marked("ValidateButton"));
    app.Screenshot("The validation results.");

    /* Assert */
    AppResult[] result = app.Query(c => c.Class("UILabel").Text("Credit card number is too long."));
    Assert.IsTrue(result.Any(), "The error message isn't being displayed.");
}

また、このテストでは、 メソッドを Screenshot 使用して、テストの実行中に重要なポイントで写真を撮影します。 このテストを実行すると、App Center によってスクリーンショットが取得され、テスト結果に表示されます。 メソッドを使用すると、テストをステップに分割し、スクリーンショットの説明を提供できます。