根據 Use Coded UI test to test your code 説明在 Visual Studio 2019 之後不再支援 Coded UI Test,需改用 Appium with WinAppDriver 測試 desktop 與 UWP apps,如果是 Xamarin 則利用 Xamarin.UITest 搭配 NUnit test framework 測試 iOS 與 Android apps。
Windows Application Driver
支援在 Windows 10 上對 UWP, WinForms, WPF 與傳統的 Win32 應用程式進行 UI 自動化測試。
WinAppDriver 搭配 Appium 建立一個 local server (預設 IP address: 127.0.0.1, port: 4723),藉由對 Appium 下指令 (HTTP Messages/APIs) 到 WebDriver 並轉換 Windows 搜尋 UI Controls 與對應的事件。概念如下:
可參考 Installing and Running Windows Application Driver 將 Windows Application Driver 安裝到 Windows 10 中,並特別注意安裝之後的目錄在:Run WinAppDriver.exe from the installation directory (E.g. C:\Program Files (x86)\Windows Application Driver)。
您可以使用方式改變監聽的 port ,如下範例:
WinAppDriver.exe 4727
WinAppDriver.exe 10.0.0.10 4725
WinAppDriver.exe 10.0.0.10 4723/wd/hub
詳細可參考 Installing and Running Windows Application Driver。根據 Set a unique automation property for UWP controls for testing 介紹,要做到 UI auto testing 需要在 App 的 UI Controls 加入 AutomationProperties 的設定:
- AutomationProperties.AutomationId
- AutomationProperties.Name
<Button Name="ButtonX" />
<Button Content="ButtonZ" />
這兩者都是隱性的處理,AutomationId 會自動使用 Control 的 Name,而 AutomationProperties.Name 則使用 Content 的内容;但是我個人建議給明確的指定,在寫測試時會比較方便,如下:
<Button AutomationProperties.AutomationId="ButtonY" />
<Button AutomationProperties.Name="ButtonZ" />
任何的 XAML Controls 都可以加入 AutomationProperties,例如 ListView 的 ListItem 可以在 binding 根據資料加上 AutomationId,例如:<ListBox Name="listBox1" ItemsSource="{Binding Source={StaticResource employees}}">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="{Binding EmployeeName}" AutomationProperties.AutomationId="{Binding EmployeeID}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
如果您想找到是 ListView 中的任何一筆資料的話,可改用 class name 的方式,例如:FindElementsByClassName。安裝好 WinAppDriver 與設定 AutomationProperties 後,接著建立 Unit Test Project 並加入 Appium.WebDriver 的 Nuget,來與 Windows Application Driver 進行 UI auto testing。
- 建立一個 .NET Framework 的 Unit Test Project,並加入 Appium.WebDriver 的 Nuget;
- 建立管理 WindowsDriver<WindowsElement> 管理 Session,並設定測試的進入點:UWP
有關於 AUMID 的取得可以從 Find the Application User Model ID of an installed app 找到方法。// 指定 AUMID 與 Appium 建立的 local server DesiredCapabilities appCapabilities = new DesiredCapabilities(); appCapabilities.SetCapability("app", "{AUMID}"); AlarmClockSession = new WindowsDriver
(new Uri("http://127.0.0.1:4723"), appCapabilities);
Classic Windows Application
使用 application 的完整執行路徑,可搭配 appArguments 傳入啓動參數與設定執行的目錄 appWorkingDir。// Launch Notepad DesiredCapabilities appCapabilities = new DesiredCapabilities(); appCapabilities.SetCapability("app", @"C:\Windows\System32\notepad.exe"); appCapabilities.SetCapability("appArguments", @"MyTestFile.txt"); appCapabilities.SetCapability("appWorkingDir", @"C:\MyTestFolder\"); NotepadSession = new WindowsDriver
(new Uri("http://127.0.0.1:4723"), appCapabilities);
其他啓動時的參數,可參考:Supported Capabilities。 - WindowsDriver<WindowsElement> 代表建立與 Appium 的連綫,需通過它向 Appium 下達指令,詳細範例:
public class RadioSession { private const string WinAppDriverUrl = "http://127.0.0.1:4723"; private const string AppId = "{AUMID}"; public WindowsDriver
Session { get; private set; } public RadioSession() { // 啓動 App 並建立 session DesiredCapabilities appCapabilities = new DesiredCapabilities(); appCapabilities.SetCapability("app", AppId); Session = new WindowsDriver (new Uri(WinAppDriverUrl), appCapabilities); // 檢查是否有正確建立 session Assert.IsNotNull(Session); Assert.IsNotNull(Session.SessionId); // 設定搜尋 element 的 timeout 時間,避免沒有回應時整個卡住 Session.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(3)); } } - 舉例找到下圖 ListView 中的任何一個 Item 並點擊它:可利用 Inspect.exe 路徑: C:\Program Files (x86)\Windows Kits\10\bin\{version}\{platform} 找到 ListView 的名字。再提醒,XAML 的 control 要記得設定 AutomationProperties 或是給與 x:Name。範例程式如下:
[TestClass] public class UnitTest1 { // 建立 Session 來保持與 Appium 互動 private readonly RadioSession session = new RadioSession(); [TestMethod] public void TestMethod1() { // 找到 ListView WindowsElement gridView = session.Session.FindElementByAccessibilityId("radioGridView"); // 找到任何一個 Item var items = gridView.FindElementsByClassName("GridViewItem"); Assert.IsTrue(items.Count > 0); // 點擊任何一個 Item items[0].Click(); } }
在搜尋 Elements 時可以參考 Supported Locators to Find UI Elements 的介紹有更多的方式。
補充
- 如果您的專案組合了 CEFSharp 的話,可以參考 CefSettings and BrowserSettings 的做法,開啓 RemoteDebuggingPort 搭配 ChromeDriver - WebDriver for Chrome的方式來測試裏面的内容。
- 如果拿到一個 App 找不到 AutomationId 時,可以搭配 Inspect.exe 來搜尋 App 中想要元件的 Id 或 Name。How can I try out WinAppDriver functionality?
- Test Project 幾個需要注意的地方:
- ClassInitialize 與 ClassCleanup每一個 TestClass 只能有一個 ClassInitialize ,它會在所有 TestMethods 被執行前優先處理;而 ClassCleanup 則是在所有 TestMethods 被完成後才會執行;常用來做一些測試前參數的準備與測試後面還原。
- TestInitialize 與 TestCleanupTestInitialize 會在每個 TestMethods 被執行前處理;TestCleanup 則是在每個 TestMethods 被完成後面執行。
- ClassInitialize 與 ClassCleanup
如果您是個人開發者也許不太會用到該篇的内容,但我建議還是能參考著使用。
因爲 UI Auto testing 導入之後,讓手動測試變成自動化,可以測試更多比較偶發的行爲。
本篇内容希望對於要入門 Windows apps 做 UI Auto testing 的人有所幫助,謝謝。
References:
- Appium The Windows Driver
- Windows Application Driver
- UI Tests for Desktop and UWP Apps
- Use UI automation to test your code
- Create a coded UI test to test a UWP app
- Set a unique automation property for UWP controls for testing
- Identify and work with Windows UI element using WinAppDriver
- Improving App Quality with UI Automation
- Windows Automation 沒有想像中的難
- MSTest v2: Test lifecycle attributes
沒有留言:
張貼留言