[Appium][WinAppDriver] Appium + WinAppDriver 測試 Windows 桌面應用程式 UI

以往我們要測試桌面應用程式得透過 CodeUI,我對他的體驗不是很好,但現在可以改用 Appium+WinAppDriver 囉,連微軟也是這麼建議,立馬來試用看看

https://docs.microsoft.com/zh-tw/visualstudio/test/use-ui-automation-to-test-your-code?view=vs-2017

甚麼是 WinAppDriver

他是一套類 Selenium 的 UI 測試框架,先來看看他的架構,由下圖可以得知 WinAppDriver 可以驅動 Windows 的應用程式,用 JSON 跟外部程式進行資料交換、控制,WinAppDriver 也整合 Appium,我們就能夠過 Appium 的語法來控制 UI

下圖出自:https://www.slideshare.net/jeremykao92/winappdriver-development

github:https://github.com/Microsoft/WinAppDriver#testing-a-universal-windows-platform-application

甚麼是 Appium

Appium 是一套開源的應用程式自動測試工具,需要更多的資訊請,參考:https://medium.com/@kentchen_tw/appium-1-app-測試自動化框架-c929d8f7a439

appium-dotnet-driver 就是直接整合原生 .NET 程式的 Library
github:https://github.com/appium/appium-dotnet-driver

本文連結

 

開發環境

  • VS 2017
  • .NET Framework 4.7.2
  • Windows 10 Enterprise ver.1803 ,OS build 17134.648

安裝及設定

設定開發模式

Windows Application Driver

  1. 下載並且安裝 https://github.com/Microsoft/WinAppDriver/releases
  2. 裝好之後路徑是 C:\Program Files (x86)\Windows Application Driver
  3. 使用管理員權限運行 WinAppDriver.exe,成功的話會建立起一個測試用的Web Server,預設是 4723,當然你也可以改

參考:https://github.com/Microsoft/WinAppDriver#installing-and-running-windows-application-driver

Inspect.exe

這是用來觀察應用程式相關的資訊,比如 Handler、Title,要先安裝 Windows 10 SDK,這可以在 Visual Studio Installer 裡面找的到,以我的環境為例,只裝了最新版本的 SDK。

在 C:\Program Files (x86)\Windows Kits\10 路徑可以看到有哪些 SDK 版本,找到 inspect.exe 然後運行,我用 C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x64\inspect.exe

他會跟著你目前的焦點,秀出那個應用程式的相關資訊,比如說:AutomationId 是 UI 上面會出現的唯一值,等會就可以透過 Appium.WebDriver 找到這個 Id

WinAppDriver UI Recorder

XPath,這是怎麼知道的呢?

下載 WinAppDriver UI Recorder v1.0 RC,https://github.com/Microsoft/WinAppDriver/releases

用可以找到滑鼠焦點的 XPath,超方便的啦,只不過用起來會鈍鈍的,可能不斷的在捕捉滑鼠鍵盤事件忙了點

還可以錄製測試步驟,轉成 C# 

實作

  • 建立一個桌面應用程式專案,不一定要建立這個專案,可以從現有的應用程式玩玩看

 

Nuget 安裝 Appium.WebDriver

建立一個單元測試專案

  • Install-Package Appium.WebDriver
  • 開始寫測試之前需要先準備測試環境

設定 App Capabilities

傳統的應用程式就傳入絕對路徑,測試專案跟桌面應用程式放在同一層目錄,這裡我為了要不直接寫死路徑,做了一些處理;先取得方案路徑在找到 app.exe,當然,可以改專案設定把 bin\debug 給拿掉

var projectName = "App";
string solutionPath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\\..\\..\\"));
var targetAppPath = Path.Combine(solutionPath, projectName, "bin", "debug", "app.exe");
 
DesiredCapabilities appCapabilities = new DesiredCapabilities();
appCapabilities.SetCapability("app", targetAppPath);

如果是 UWP 應用程式則是傳入Package ID,比如

appCapabilities.SetCapability("app", "Microsoft.WindowsAlarms_8wekyb3d8bbwe!App");

更多的設定,請參考:https://github.com/Microsoft/WinAppDriver#supported-capabilities

建立 WindowsDriver

傳入 Windows App Driver 的 URI 

WindowsDriver = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), appCapabilities);

 

更多的設定,請參考https://github.com/Microsoft/WinAppDriver#testing-a-classic-windows-application

前置作業的完整代碼如下

開始測試之前建置 WindowsDriver 環境,結束之後關掉 WindowsDriver

[TestClass]
public class UnitTest1
{
    protected internal static WindowsDriver<WindowsElement> WindowsDriver;
 
    [ClassInitialize]
    public static void Setup(TestContext context)
    {
        var projectName = "App";
        string solutionPath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\\..\\..\\"));
        var targetAppPath = Path.Combine(solutionPath, projectName, "bin", "debug", "app.exe");
 
        DesiredCapabilities appCapabilities = new DesiredCapabilities();
        appCapabilities.SetCapability("app", targetAppPath);
        WindowsDriver = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), appCapabilities);
    }
 
    [ClassCleanup]
    public static void Cleanup()
    {
        WindowsDriver.CloseApp();
        WindowsDriver.Dispose();
    }
}

 

開始寫測試

測試案例=需求,有需求才有測試

案例步驟

  • 在 Id 文字方塊輸入yao
  • 在 Password 文字方塊輸入123456
  • 按下 Login 按鈕
  • 預期得到一個彈跳視窗並呈現 Hi~yao

如下,翻成測試程式

[TestMethod] public void 輸入帳號密碼_按下登入_預期得到一個彈跳視窗並呈現Hiyao()
{
    WindowsDriver.FindElementByAccessibilityId("Id_TextBox").SendKeys("yao");
    WindowsDriver.FindElementByAccessibilityId("Password_TextBox").SendKeys("123456");
    WindowsDriver.FindElementByAccessibilityId("Login_Button").Click();
 
    var messageBox = WindowsDriver.FindElementByClassName("#32770");
    var title = messageBox.Text;
    var messageText = messageBox.FindElementByXPath("//Text[@Name='Hi~yao']").Text;
 
    Assert.AreEqual("Title", title);
    Assert.AreEqual("Hi~yao", messageText);
 
    WindowsDriver.FindElementByXPath("//Button[@Name='OK']").Click();
}

 

桌面應用程式的驗證,就是看那一個視窗的效果是否如預期,視窗、控制項都有一個唯一值 Handler,只要有這個就能下 Send/Post Message 存取它,現在我們藉由 FindElement 幫我們找出 Handler並且存取,簡單多了!

Appium API

Appium API 提供了一些常用的搜尋讓我們能快速的找到 UI 控制項

FindElementByAccessibilityId:根據 Id 找控制項,對應到 Inspect 的 AutomationId,這個是唯一值,用它最好找

FindElementByClassName根據 Class Name 找控制項,對應到 Inspect 的 ClassName,可能不是唯一。

FindElementByXPath:當我們用到 DataBinding ,讓動態長出來的 UI,不會有唯一值,這時候就可以用這個。

 

Appium API 所對應到 Inspect 的 屬性如下表,出自 https://github.com/Microsoft/WinAppDriver#supported-locators-to-find-ui-elements


若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo