Xamarin.UITest

重要

Visual Studio App Center 已排定於 2025 年 3 月 31 日淘汰。 雖然您可以繼續使用 Visual Studio App Center,直到它完全淘汰為止,但有數個建議您考慮移轉至的建議替代方案。

深入瞭解支持時程表和替代方案。

Xamarin.UITest 是 C# 測試架構,使用 NUnit 進行 iOS 和 Android 應用程式的 UI 接受測試。 它與 Xamarin.iOS 和 Xamarin.Android 項目緊密整合,但也可以與原生 iOS 和 Android 專案搭配使用。 Xamarin.UITest 是自動化連結 ,可讓 NUnit 測試在 Android 和 iOS 裝置上執行。 測試會以使用者身分與使用者介面互動:輸入文字、點選按鈕和手勢,例如撥動。

一般而言,每個 Xamarin.UITest 都會撰寫為稱為 [Test]的方法。 包含測試類別稱為 [TestFixture]。 測試裝置包含單一測試或測試群組。 裝置也負責設定,讓測試回合和清除完成測試時必須完成。 每個測試都應該遵循 Arrange-Act-Assert 模式:

  1. 排列:測試會設定條件並初始化專案,以便執行測試。
  2. 動作:測試會與應用程式互動、輸入文字、按下按鈕等等。
  3. 判斷提示:測試會檢查動作在 Act 步驟中執行的結果,以判斷正確性。 例如,應用程式可能會確認顯示特定的錯誤訊息。

開始使用 Xamarin.UITest 的最佳時機是在開發行動應用程式期間。 自動化測試是以功能的形式撰寫,是根據下列清單中所述的步驟進行開發:

  1. 在 Android 或 iOS 應用程式中開發功能。
  2. 撰寫測試並在本機執行以驗證功能。
  3. 在 App Center 測試中建立新的測試回合,或使用現有的測試回合。
  4. 編譯 IPA 或 APK,然後將它連同測試一起上傳至 App Center 測試。
  5. 修正 App Center 測試所公開的任何問題或錯誤。
  6. 移至應用程式的下一個功能,以重複此程式。

對於不再處於使用中開發狀態的現有應用程式,回溯新增自動化測試可能不符合成本效益。 相反地,更好的方法是在修正 Bug 時使用 Xamarin.UITest。 例如,請考慮沒有自動化測試的應用程式,而使用者回報錯誤。 指派修正 Bug 的開發人員可能會採取下列動作的一些 (或所有) :

  • 手動驗證 Bug 或回歸。
  • 使用示範 Bug 的 Xamarin.UITest 撰寫測試。
  • 將測試提交至 App Center 測試,以深入瞭解相關裝置上 Bug 的範圍和影響。
  • 修正 Bug。
  • 證明已使用傳遞的 Xamarin.UITest 來修正 Bug。
  • 將修正程式和測試提交至 App Center 測試,以確認已修正相關裝置上的 Bug。
  • 檢查將測試傳遞至版本控制。

自動化UI測試高度依賴在畫面上尋找和互動檢視。 Xamarin.UITest 可透過兩組重要的 API 來解決此需求,這些 API 彼此搭配使用:

  1. 可在檢視上完成的動作 - Xamarin.UITest 提供 API,允許測試模擬常見的用戶動作,例如點選檢視、輸入文字或撥動檢視。
  2. 畫面上尋找檢視的查詢 - Xamarin.UITest 架構的一部分是可在畫面上找到檢視的 API。 查詢會檢查檢視的屬性,並傳回動作可能運作的物件,以在運行時間尋找檢視。 以這種方式查詢是功能強大的技術,可讓測試針對任何螢幕大小、方向或配置的使用者介面撰寫

為了協助撰寫測試,Xamarin.UITest 提供 read-eval-print-loop (REPL) 。 REPL 可讓開發人員和測試人員在執行應用程式時與畫面互動,並簡化建立查詢。

Xamarin.UITest API 簡介

所有與行動應用程式的測試互動都會透過的 Xamarin.UITest.IApp實例進行。 此介面會定義測試與應用程式共同作業並與使用者介面互動的重要方法。 此介面有兩個具體實作:

  • Xamarin.UITest.iOS.iOSApp 此類別會將 iOS 的測試自動化。
  • Xamarin.UITest.Android.AndroidApp 此類別適用於在Android上自動化測試。

iOSAppAndroidApp 物件不會直接具現化。 而是使用協助程式 ConfigureApp 類別來建立它們。 這個類別是一種產生器,可確保 iOSAppAndroidApp 已正確具現化。

建議您針對每個測試使用新的 IApp 實例。 新的實例可防止狀態從一個測試溢出到另一個測試。 NUnit 測試可以初始化 的 IApp實例有兩個位置:

  • SetUp 方法 中,測試裝置通常是相關測試的邏輯群組,每個測試都是彼此獨立執行。 在此案例中SetUpIApp應該在方法中初始化 ,以確保每個測試都有新的 IApp 可用。
  • TestFixtureSetup在方法 中,在某些情況下,單一測試可能需要自己的測試裝置。 在此情況下,在方法中TestFixtureSetup初始化IApp物件一次可能更合理。

設定好之後 IApp ,測試可能會開始與所測試的應用程式互動。 若要這樣做,您必須取得螢幕上可見之檢視的參考。 Xamarin.UITest 中的許多方法都會採用 Func<AppQuery, AppQuery> 參數來找出檢視。 例如,下列代碼段示範如何點選按鈕:

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

Xamarin.UITest 架構中有兩個介面實 IApp 作,一個適用於 iOS,另一個用於 Android。

初始化 iOS 應用程式的 IApp

當 Xamarin.UITest 在 iOS 上執行測試時,它會啟動 iOS 模擬器的實例、部署應用程式、啟動它,並開始執行測試。 iOS 應用程式必須已經建置。 Xamarin.UITest 不會編譯應用程式,併為您建立應用程式套件組合。

AppBundle方法可用來指定可在檔案系統上找到應用程式套件組合的位置。 有兩種方式可以執行這項操作,使用絕對路徑或相對路徑。 這個代碼段顯示使用應用程式套件組合的絕對路徑:

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 元件向上移至三個目錄,然後流覽 iOS 應用程式專案的專案樹狀結構,以尋找應用程式套件組合。

ConfigureApp 有其他方法來協助設定 IApp。 如需詳細資訊,請參閱 iOSAppConfigurator 類別。 下表說明一些更有趣的方法:

方法 Description
AppBundle 這個方法會指定測試時要使用的應用程式套件組合路徑。
Debug 此方法會在測試執行器中啟用偵錯記錄訊息。 此方法有助於針對在模擬器上執行應用程式的問題進行疑難解答。
DeviceIdentifier 將裝置設定為與裝置標識碼搭配使用。 以下將詳細說明這個方法。
EnableLocalScreenshots 在本機執行測試時啟用螢幕快照。 測試在雲端中執行時,一律會啟用螢幕快照。

如需如何在特定 iOS 模擬器上執行 iOS 測試的詳細資訊,請參閱 判斷 iOS 模擬器的裝置標識碼

初始化適用於Android應用程式的IApp

Xamarin.UITest 會將現有的 APK 部署到已連結的裝置或已執行的 Android 模擬器實例。 應用程式將會啟動,然後執行測試。 Xamarin.UITest 無法建置 APK,也無法啟動 Android 模擬器的實例。

ApkFile的 方法是IApp用來指定可在檔案系統上找到 APK 的位置。 有兩種方式可以執行這項操作,使用絕對路徑或相對路徑。 此代碼段會顯示使用 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 元件向上移出三個目錄,然後向覽 Android 應用程式專案的專案樹狀結構,以尋找 apk 檔案。

如果有一個以上的裝置或模擬器連線,Xamarin.UITest 將會停止測試執行,並顯示錯誤訊息,因為無法解決測試的預期目標。 在此情況下,您必須提供裝置或模擬器的 序列標識碼 來執行測試。 例如,請考慮命令的 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> 委派來尋找檢視。 此委派會使用 AppQuery Xamarin.UITest 如何尋找檢視的核心。

AppQuery 是用來建置查詢以尋找檢視的 Fluent 介面 。 在提供的方法 AppQuery 中,方法 Marked 是最簡單且最有彈性的方法之一。 此方法會使用啟發學習法來嘗試尋找檢視,並在下一節中更詳細地討論。 目前,請務必瞭解 IApp 有許多方法可與應用程式互動。 這些方法會使用 Func<AppQuery, AppQuery> 取得要與其互動之檢視的參考。 下面列出一 AppQuery 些更有趣的方法:

方法 Description
Button 會在畫面上找到一或多個按鈕。
Class 將會嘗試找出屬於指定類別的檢視。
Id 將會嘗試找出具有指定標識碼的檢視。
Index . 會從相符檢視的集合傳回一個檢視。 通常與其他方法搭配使用。 採用以零起始的索引。
Marked 會根據下面討論的啟發學習法傳回檢視。
Text 將會比對包含所提供文字的檢視。
TextField 將會比對 Android EditText 或 iOS UITextField

例如,下列方法示範如何模擬名為 「SaveUserdataButton」 的按鈕點選:

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

因為 AppQuery 是 Fluent 介面,所以可以將多個方法調用鏈結在一起。 請考慮點選檢視的這個更複雜的範例:

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

在這裡, AppQuery 會先尋找標示 Pending為 的檢視,然後選取該檢視的第一個 AppointmentListCell 父系,該檢視是類型。

藉由查看行動應用程式,嘗試建立這些查詢可能會很棘手。 Xamarin.UITest 提供 REPL,可用來探索畫面的檢視階層、實驗建立查詢,並使用它們來與應用程式互動。

使用 REPL

啟動 REPL 的唯一方法是在現有的測試中叫 IApp.Repl 用 方法。 這需要建立 NUnit TestFixture,設定 可用於 方法的 Test 實體IApp。 下列代碼段示範如何執行這項操作的範例:

[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 已初始化名為 的IAppapp實例,該實例會與應用程式互動。 第一件事之一是探索使用者介面。 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"
>>>

我們可以看到 UIButton 此檢視 id 中有 ValidateButton 的 。 我們可以使用 命令所 tree 顯示的信息,協助製作必要的查詢,以找出檢視並與其互動。 例如,下列程式代碼會模擬按鈕上的點選:

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

當輸入命令時,REPL 會在緩衝區中記住這些命令。 REPL 提供命令 copy ,將這個緩衝區的內容複製到剪貼簿。 這可讓我們建立測試的原型。 我們可以使用 copy將 REPL 中完成的工作複製到剪貼簿,然後在 中 [Test]貼上這些命令。

使用標示尋找檢視

AppQuery.Marked 方法是方便且功能強大的方法來查詢螢幕上的檢視。 其運作方式是檢查畫面上檢視的檢視階層,嘗試比對檢視的屬性與提供的字串。 Marked 會根據操作系統以不同的方式運作。

尋找已標示的 iOS 檢視

iOS 檢視會使用下列其中一個屬性來尋找:

  • 檢視 AccessibilityIdentifier
  • 檢視 AccessibilityLabel

例如,請考慮下列 C# 代碼段來建立 UILabel 並設定 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:id 此按鈕的 是action1_buttonandroid:text 而 是 動作 1。 下列兩個查詢中的任一項會在畫面上找到按鈕:

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

使用 Xamarin.UITest.IApp 控制應用程式

設定並初始化之後 IApp ,測試可能會開始與應用程式互動。 使用 Func<AppQuery, AppQuery> 的方法其中一個範例是 IApp.Query() 方法。 這個方法會執行查詢並傳回結果。 下列代碼段顯示最簡單的範例,其會傳回畫面上可見的所有檢視清單:

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

下表示範使用 AppQuery 來尋找螢幕上檢視的一些其他範例:

Syntax 結果
app.Query(c=>c.Class("UILabel")) 方法 .Class() 會查詢屬於 iOS UILabel子類別的檢視。
app.Query(c=>c.Id("txtUserName")) 方法 .Id() 會查詢具有 IdtxtUserName 的檢視。
app.Query(c=>c.Class("UILabel").Text("Hello, World")) UILabel找出所有具有文字 「Hello, World」 的類別。
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,請參閱 、 AndroidAppiOSAppIAppAPI 檔

作為如何使用這些方法的範例,請考慮下列測試上方顯示的螢幕快照。 此測試會在文字欄位中輸入信用卡的 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 會擷取螢幕快照,並在測試結果中顯示它們。 方法允許將測試分成步驟,並提供螢幕快照的描述。