UI自動化 --- 微軟UI Automation

2023-07-09 21:00:29

引言

自動化測試平臺的意義就三個字 --- 穩定性。

無論是介面自動化測試,還是UI自動化測試,目的就是為了提高產品的穩定性,保證使用者體驗。

那常見的介面自動化測試比如有 PostmanSoapUIJMeter 等等。這一類網上的資料就太多太多了。本篇內容主要想討論的是UI自動化測試,我搜集了一下常見的UI自動化測試平臺:

  • Selenium:Selenium是開源且免費的,使用非常廣泛的Web應用程式自動化測試框架,它既支援多種語言的指令碼驅動測試,也支援記錄與回放的方式測試。
  • Appium:Appium是一個開源免費的移動應用程式自動化測試框架,也支援多種語言的指令碼驅動測試。
  • Katalon Studio: Katalon Studio是一款免費的UI自動化測試工具,適用於Web、移動和API測試。它基於Selenium和Appium,並提供了圖形化介面和內建的測試功能。
  • TestComplete:TestComplete是一款功能強大的商業UI自動化測試工具,適用於Web、桌面和移動應用程式。它提供了多種指令碼語言和圖形化介面,以及靈活的物件識別和回放功能。
  • Ranorex: Ranorex是一款商業UI自動化測試工具,適用於Web、桌面和移動應用程式。它提供了易於使用的錄製和回放功能,支援多種程式語言。

從上面的資料中其實不難發現,對於桌面應用的UI自動化測試,上述框架或工具要麼免費但是僅支援Web應用,要麼就是商業化工具,一言難盡啊。

所以這就提到了我們的主題了 --- UIAutomation。

UIAutomation 介紹

來看一下微軟官方對此的介紹 UIAutomation:

Microsoft UI Automation是適用於Microsoft Windows的輔助功能框架。它滿足了輔助技術產品和自動化測試框架的需求,通過提供對使用者介面(UI)資訊的程式設計存取來實現。此外,UI Automation還使控制元件和應用程式開發人員能夠使其產品具有輔助功能。

裡邊提到了,使用程式設計存取可以通過程式碼模仿由傳統滑鼠和鍵盤輸入展開的任何互動和體驗,UIAutomation 通過五個元件實現程式設計存取:

  • UI Automation tree(UI自動化樹)
  • UI Automation elements(UI自動化元素)
  • UI Automation properties(UI自動化屬性)
  • Control patterns(控制元件模式)
  • UI Automation events(UI自動化事件)

如下圖所示:

最後列出了測試應用程式中實現UI自動化的步驟:

輔助工具介紹

步驟中提到了一個工具 --- Inspect.exe ,該工具是一個圖形化使用者介面 (GUI) 應用程式,可用於收集用於提供程式和使用者端開發和偵錯的 UI 自動化資訊,它包含在 Windows SDK 中。

所以要使用Inspect.exe去查詢控制元件的 AutomationIdProperty,必須安裝Windows SDK。

然後可以直接使用Everything直接搜尋 Inspect.exe ,可以看到 SDK 安裝目錄下,分別有x64,x86,arm64,arm四個,我們一般選擇 x64 就可以。

然後使用該工具可以找到目標應用的控制元件,如下圖,找到了按鈕的AutomationID 就可以進行編碼模擬點選。

小試牛刀

接下來編寫程式碼測試一下:

  1. 建立目標程式,一個WPF程式,放置一個TextBox輸入框,命名為 textbox1,再放置一個按鈕,命名為 button1,button1點選後彈窗提示「UIAutomation按鈕測試」。
  2. 接下來建立測試程式,建立一個控制檯程式,使用 UIAutomation API 執行在點選button1textbox1文字方塊輸入"UIAutomation按鈕測試"字尾時間。
  3. 使用Inspect.exe找到目標程式的的程序ID和控制元件的AutomationID。輸入到測試程式中,看執行結果。

測試控制檯程式程式碼如下:

using System;
using System.Diagnostics;
using System.Windows.Automation;

public class ProcessButtonClickAutomation
{
    public static void Main()
    {
        Console.WriteLine("請輸入目標程序ID:");
        var targetProcessId = Console.ReadLine();
        Console.WriteLine("請輸入目標控制元件AutomationId:");
        var automationId = Console.ReadLine();

        // 根據程序ID查詢程序
        Process targetProcess = FindProcessById(int.Parse(targetProcessId));

        if (targetProcess != null)
        {
            // 查詢程序的主視窗控制程式碼
            IntPtr mainWindowHandle = targetProcess.MainWindowHandle;

            if (mainWindowHandle != IntPtr.Zero)
            {
                // 使用主視窗控制程式碼獲取AutomationElement
                AutomationElement mainWindowElement = AutomationElement.FromHandle(mainWindowHandle);

                // 查詢按鈕元素,根據需要修改按鈕查詢條件
                PropertyCondition condition = new PropertyCondition(AutomationElement.AutomationIdProperty, automationId);
                AutomationElement buttonElement = mainWindowElement.FindFirst(TreeScope.Descendants, condition);

                if (buttonElement != null)
                {
                    // 獲取按鈕元素的InvokePattern
                    InvokePattern invokePattern = buttonElement.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;

                    if (invokePattern != null)
                    {
                        // 模擬按鈕點選操作
                        invokePattern.Invoke();
                        Console.WriteLine("按鈕已被點選  " + DateTime.Now);
                    }
                    else
                    {
                        Console.WriteLine("按鈕不可用");
                    }
                }
                else
                {
                    Console.WriteLine("找不到按鈕元素");
                }
            }
            else
            {
                Console.WriteLine("找不到程序的主視窗");
            }
        }
        else
        {
            Console.WriteLine("找不到指定的程序");
        }
        Console.WriteLine("執行完成~");

        Console.ReadLine();
    }

    // 根據程序ID查詢程序
    public static Process FindProcessById(int processId)
    {
        try
        {
            Process process = Process.GetProcessById(processId);
            return process;
        }
        catch (ArgumentException)
        {
            return null;
        }
    }
}

使用Inspect.exe可以看到目標WPF程式的 ProcessID = 24984 ,按鈕控制元件的 AutomationId = button1

在控制檯輸入 ProcessIDAutomationId 可以看到結果測試程式執行結束後,目標WPF程式的文字方塊同時重新整理。

寫在結尾

UI Automation說是框架,我覺得更像是一組API,只是提供了你能夠做自動化測試的基本能力,如果想要搭建一個桌面應用的UI自動化測試平臺,那需要做的還有很多。比如你需要實現指令碼支援,需要實現執行報告和紀錄檔,需要實現資料驅動測試,甚至需要支援持續整合和持續交付(CI/CD)流程中自動執行UI自動化測試。

現在我想搭建一個基於 UI Automation 的桌面應用的UI自動化測試平臺,現在只是有一個大體思路:

  • UI Automation 提供桌面應用自動化測試的基本能力。
  • Roslyn 編譯器平臺提供指令碼支援。
  • 執行報告和紀錄檔在封裝UI Automation API的過程中,就可以新增進去了
  • 資料驅動測試就可以在指令碼中完成。
  • 最後一點,持續整合和持續交付可以交給Jenkins

還是文章開頭說到的,於桌面應用的UI自動化測試,現有框架或工具要麼免費但是僅支援Web應用,要麼就是商業化工具,所以想自己整一個,想法和方案都有了,至於可以做成什麼樣,那就拭目以待吧。

不瞭解 Roslyn 編譯器平臺的,可以看我之前的兩篇文章

參考連結

Using UI Automation for Automated Testing: https://learn.microsoft.com/en-us/dotnet/framework/ui-automation/using-ui-automation-for-automated-testing

.Net 編譯器平臺 --- Roslyn(https://niuery.com/post/67)
.Net 編譯器平臺--- Roslyn Scripting APIs(https://niuery.com/post/68)