Xamarin.UITest

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

一般來說,每個 UITest 都是以稱為的方法來撰寫 [Test] 。 包含測試的類別稱為 [TestFixture] 。 測試裝置包含單一測試或一組測試。 這些裝置也會負責安裝程式,以便在測試完成時,執行需要完成的測試執行和清除。 每個測試都應遵循 排列-Act 判斷提示模式:

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

開始使用 Xamarin 的最佳時機是在開發行動應用程式的過程中,UITest。 系統會根據下列清單中所述的步驟,將自動化測試寫入為正在開發的功能:

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

對於不再使用開發的現有應用程式,追溯新增自動化測試可能不會符合成本效益。 相反地,更好的方法是在修正 bug 時使用 UITest。 例如,假設應用程式沒有自動化測試,而且使用者報告了錯誤。 指派來修正 bug 的開發人員可能會採取一些 (或所有) 的下列動作:

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

自動化 UI 測試主要依賴于尋找畫面上的視圖並與之互動。 UITest 使用兩個重要的 Api 集合來解決這項需求:

  1. 可以在 views 上完成的 動作-Xamarin. UITest 提供的 api 可讓測試模擬一般使用者動作,例如,在視圖上進行點擊、輸入文字,或在觀看時輕刷。
  2. 在 UITest 架構的螢幕上尋找視圖的 查詢,是將在螢幕上尋找視圖的 api。 查詢會在執行時間尋找視圖,方法是檢查視圖的屬性,並傳回動作可能作用的物件。 以這種方式查詢是一種強大的技術,可讓您針對任何螢幕大小、方向或版面配置的使用者介面撰寫測試。

為了協助撰寫測試,UITest 提供了 讀取-eval-print 迴圈 (複寫) 。 複寫可讓開發人員和測試人員在應用程式執行時與螢幕互動,並簡化查詢的建立工作。

UITest API 簡介

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

  • Xamarin.UITest.iOS.iOSApp 此類別會將對 iOS 的測試自動化。
  • Xamarin.UITest.Android.AndroidApp 此類別是用來將 Android 上的測試自動化。

iOSAppAndroidApp 物件不會直接具現化。 相反地,它們是使用 helper 類別來建立 ConfigureApp 。 這個類別是一個產生器,可確保 iOSAppAndroidApp 已正確具現化。

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

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

設定完成之後 IApp ,測試就可以開始與所測試的應用程式互動。 若要這樣做,必須取得螢幕上可見的視圖參考。 Xamarin. UITest 中的許多方法都採用 Func<AppQuery, AppQuery> 參數來尋找視圖。 例如,下列程式碼片段顯示如何使用按鈕:

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

UITest 架構中有兩個 IApp 介面,一個用於 iOS,另一個用於 Android。

針對 iOS 應用程式初始化 IApp

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

AppBundle方法可以用來指定可以在檔案系統上找到應用程式套件組合的位置。 有兩種方式可以使用絕對路徑或相對路徑。 此程式碼片段顯示如何使用應用程式套件組合的絕對路徑:

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

部分路徑必須相對於 UITest 元件。 此程式碼片段為範例:

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

相對路徑範例會告訴您 AppBundle 從 UITest 元件向上移至三個目錄,然後向下流覽至 iOS 應用程式專案的專案樹狀結構,以尋找應用程式套件組合。

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

方法 描述
AppBundle 這個方法會指定測試時要使用的應用程式套件組合路徑。
Debug 這個方法會在測試執行器中啟用 debug 記錄訊息。 針對在模擬器上執行應用程式的問題進行疑難排解時,這個方法很有用。
DeviceIdentifier 設定要與裝置識別碼搭配使用的裝置。 以下將更詳細說明這個方法。
EnableLocalScreenshots 在本機執行測試時啟用螢幕擷取畫面。 在雲端中執行測試時,一律會啟用螢幕擷取畫面。

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

針對 Android 應用程式初始化 IApp

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

ApkFile 方法 IApp 是用來指定可以在檔案系統上找到 APK 的位置。 有兩種方式可以使用絕對路徑或相對路徑。 此程式碼片段顯示如何使用 APK 的絕對路徑:

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

部分路徑必須相對於 UITest 元件。 此程式碼片段為範例:

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

相對路徑範例會告訴您 ApkFile 從 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 會使用 UITest 尋找視圖的核心。

AppQuery 是用來建立查詢以找出視圖的 流暢介面 。 針對提供的方法 AppQueryMarked 方法是最簡單且最具彈性的方法之一。 這個方法會使用啟發學習法來嘗試找出視圖,並將在下一節中更詳細地討論。 現在,請務必瞭解, IApp 有許多方法可與應用程式互動。 這些方法會使用 Func<AppQuery, AppQuery> 來取得要與之互動的視圖參考。 提供的一些更有趣的方法如下所示 AppQuery

方法 描述
Button 會在螢幕上找出一或多個按鈕。
Class 將會嘗試找出屬於指定類別的 views。
Id 將會嘗試尋找具有指定識別碼的視圖。
Index . 將會從相符視圖的集合傳回一個 view。 通常與其他方法搭配使用。 取得以零為基底的索引。
Marked 會根據下面討論的啟發學習法來傳回視圖。
Text 將符合包含所提供文字的視圖。
TextField 將符合 Android EditText 或 iOS UITextField

例如,下列方法顯示如何模擬名為 "SaveUserdataButton" 的按鈕:

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

由於 AppQuery 是流暢的介面,因此可以將多個方法調用串連在一起。 請考慮這個更複雜的點擊查看範例:

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

在這裡, AppQuery 會先尋找標示的視圖 Pending ,然後選取該視圖的第一個父代,也就是一 AppointmentListCell 種類型。

藉由查看行動應用程式來嘗試建立這些查詢,可能會很難。 UITest 會提供一個複寫,可用來流覽螢幕的視圖階層、嘗試建立查詢,以及使用它們來與應用程式互動。

使用複製

開始複寫的唯一方法是 IApp.Repl 在現有的測試中叫用方法。 這需要建立 NUnit,並設定 TestFixture IApp 可用於方法的實例 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 方法時,UITest 將會啟動終端機會話中的複寫,如下列螢幕擷取畫面所示:

MacOS 終端機執行 Xamarin. UITest 複寫的螢幕擷取畫面

複寫已初始化 IApp 稱為的實例,該實例會 app 與應用程式互動。 首先要做的事,就是探索使用者介面。 複寫有命令可進行此作業 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&quot;
      [_UITextContainerView]
    [UIButton] id: &quot;ValidateButton&quot;
      [UIButtonLabel] text: &quot;Validate Credit Card&quot;
    [UILabel] id: &quot;ErrorrMessagesTestField&quot;
  [UINavigationBar] id: &quot;Credit Card Validation&quot;
    [_UINavigationBarBackground]
      [_UIBackdropView > _UIBackdropEffectView]
      [UIImageView]
    [UINavigationItemView]
      [UILabel] text: &quot;Credit Card Validation&quot;
>>>

我們可以 UIButton 在這個視圖中看到 id ValidateButton 的。 我們可以使用命令所顯示的資訊 tree 來協助製作必要的查詢,以找出並與視圖互動。 例如,下列程式碼會模擬按鈕的點擊:

app.Tap(c=>c.Marked(&quot;ValidateButton"))

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

使用標記以找出視圖

AppQuery 標示的方法是在螢幕上查詢檢視的方便且功能強大的方式。 其運作方式是檢查畫面上某個視圖的視圖階層,並嘗試將視圖上的屬性與所提供的字串進行比對。 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 views 將根據下列其中一個屬性來找出:

  • 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_button ,而且 android: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()方法會查詢具有 Id txtUserName 的 views。
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 應用程式中,UITest 會使用軟鍵盤來輸入文字。 相反地,UITest 不會使用 Android 鍵盤,它會直接將文字輸入到視圖中。
WaitForElement 暫停測試的執行,直到視圖出現在螢幕上。
Screenshot(String) 取得應用程式目前狀態的螢幕擷取畫面,並將其儲存至磁片。 它會傳回 FileInfo 物件,其中包含所拍攝螢幕擷取畫面的相關資訊。
Flash 這個方法會讓選取的視圖在畫面上顯示為「閃爍」或「閃爍」。

如需介面的詳細資訊 IApp ,請參閱、和的 API 檔 IApp AndroidApp iOSApp

如需如何使用這些方法的範例,請考慮下列顯示的螢幕擷取畫面測試。 這項測試會將信用卡的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 將會拍攝螢幕擷取畫面,並將其顯示在測試結果中。 方法可讓您將測試細分為步驟,並提供螢幕擷取畫面的說明。