WinAppDriver 自动化测试winform程序
前言
WinAppDriver是Windows系统上的一个应用程序驱动工具,开源免费
。与Selenium工具类似,都是用来实现产品UI自动化测试的一个工具。
WinAppDriver运行时对系统是有要求的,只能运行在Windows10或Windows Server 2016以上系统。如果测试程序兼容性,WinAppDriver很显然不能满足Windows10或Windows Server 2016以下系统的测试。因此使用WinAppDriver实现的自动化测试脚本是有局限性的。
WinAppDriver支持测试UWP、WinForms、WPF、Win32应用程序。
UWP: Universal Windows Platform,即Windows通用应用平台,在Windows 10 Mobile/Surface(Windows平板电脑)/PC/Xbox/HoloLens等平台上运行。它并不是为某一个终端而设计,而是可以在所有Windows10设备上运行。
WinForms: Windows Forms,是微软的.NET开发框架的图形用户界面部分,该组件通过将现有的Windows API(Win32 API)封装为托管代码提供了对Windows本地(native)组件的访问方式。
WPF: Windows Presentation Foundation,是微软推出的基Windows的用户界面框架,属于.NET Framework 3.0的一部分。它提供了统一的编程模型、语言和框架,真正做到了分离界面设计人员与开发人员的工作;同时它提供了全新的多媒体交互用户图形界面。
Win32: Classic Windows,是标准windows程序,完全拥有window的特性,可以通过鼠标点击窗口来完成控制。
1. 环境搭建
前提条件
电脑系统需要Windows 10或Windows Server 2016或者更高版本,这是前提条件
1.1 打开Windows PC的开发者模式
1.2 下载Windows driver并安装
github下载地址:https://github.com/Microsoft/WinAppDriver/releases
选择与你电脑对应的exe安装
安装好之后,运行WinAppDriver.exe
(记得要用admin权限运行), 默认路径 (C:\Program Files (x86)\Windows Application Driver)
也可以自定义地址或端口:
在cmd窗口中输入
WinAppDriver.exe 4727
WinAppDriver.exe 127.0.0.1 4725
WinAppDriver.exe 127.0.0.1 4723/wd/hub
这样就说明运行成功
2. Windows 自动化脚本
运行脚本前要打开 WinAppDriver.exe
对于Windows App来说,只需要传一个app capabilities 即可。
对于UWP的App,app对应的值为Application Id(App ID)。关于如何获取APP ID,可以使用powershell
命令get-StartApps
来获取,打开powershell终端运行:get-StartApps | select-string "计算器"
即可获取值(运行命令之前先打开计算器)。
DesiredCapabilities appCapabilities = new DesiredCapabilities();
appCapabilities.SetCapability("app", CalculatorAppId);
appCapabilities.SetCapability("deviceName", "WindowsPC");
appCapabilities.SetCapability("platformName", "Windows");
session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), appCapabilities);
3. Windows定位元素
使用Windows SDK提供的工具inspect.exe(C:\Program Files (x86)\Windows Kits\10\bin\x86或者C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64
根据系统查看)来定位,详情查看inspect,或者使用AccExplorer32、UISpy定位。
支持的定位方式:
4. 示例
这个是官方给的示例
CalculatorSession.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Remote;
using System;namespace CalculatorTest
{public class CalculatorSession{// Note: append /wd/hub to the URL if you're directing the test at Appiumprivate const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";private const string CalculatorAppId = "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App";protected static WindowsDriver<WindowsElement> session;public static void Setup(TestContext context){// Launch Calculator application if it is not yet launchedif (session == null){// Create a new session to bring up an instance of the Calculator application// Note: Multiple calculator windows (instances) share the same process IdDesiredCapabilities appCapabilities = new DesiredCapabilities();appCapabilities.SetCapability("app", CalculatorAppId);appCapabilities.SetCapability("deviceName", "WindowsPC");appCapabilities.SetCapability("platformName", "Windows");session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), appCapabilities);Assert.IsNotNull(session);// Set implicit timeout to 1.5 seconds to make element search to retry every 500 ms for at most three timessession.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1.5);}}public static void TearDown(){// Close the application and delete the sessionif (session != null){ session.Quit();session = null;}}}
}
ScenarioStandard.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
using System.Threading;
using System;namespace CalculatorTest
{[TestClass]public class ScenarioStandard : CalculatorSession{private static WindowsElement header;private static WindowsElement calculatorResult;[TestMethod]public void Addition(){// Find the buttons by their names and click them in sequence to perform 1 + 7 = 8session.FindElementByName("一").Click();session.FindElementByName("加").Click();session.FindElementByName("七").Click();session.FindElementByName("等于").Click();Assert.AreEqual("8", GetCalculatorResultText());}[TestMethod]public void Division(){// Find the buttons by their accessibility ids and click them in sequence to perform 88 / 11 = 8session.FindElementByAccessibilityId("num8Button").Click();session.FindElementByAccessibilityId("num8Button").Click();session.FindElementByAccessibilityId("divideButton").Click();session.FindElementByAccessibilityId("num1Button").Click();session.FindElementByAccessibilityId("num1Button").Click();session.FindElementByAccessibilityId("equalButton").Click();Assert.AreEqual("8", GetCalculatorResultText());}[TestMethod]public void Multiplication(){// Find the buttons by their names using XPath and click them in sequence to perform 9 x 9 = 81//session.FindElementByXPath("//Button[@Name='Nine']").Click();//session.FindElementByXPath("//Button[@Name='Multiply by']").Click();//session.FindElementByXPath("//Button[@Name='Nine']").Click();//session.FindElementByXPath("//Button[@Name='Equals']").Click();session.FindElementByAccessibilityId("num9Button").Click();session.FindElementByAccessibilityId("num9Button").Click();session.FindElementByAccessibilityId("multiplyButton").Click();session.FindElementByAccessibilityId("num9Button").Click();session.FindElementByAccessibilityId("equalButton").Click();Assert.AreEqual("891", GetCalculatorResultText());}[TestMethod]public void Subtraction(){// Find the buttons by their accessibility ids using XPath and click them in sequence to perform 9 - 1 = 8session.FindElementByXPath("//Button[@AutomationId=\"num9Button\"]").Click();session.FindElementByXPath("//Button[@AutomationId=\"minusButton\"]").Click();session.FindElementByXPath("//Button[@AutomationId=\"num1Button\"]").Click();session.FindElementByXPath("//Button[@AutomationId=\"equalButton\"]").Click();Assert.AreEqual("8", GetCalculatorResultText());}[TestMethod][DataRow("一", "加", "九", "10")][DataRow("九", "减", "二", "8")][DataRow("八", "除以", "二", "4")]public void Templatized(string input1, string operation, string input2, string expectedResult){// Run sequence of button presses specified above and validate the resultssession.FindElementByName(input1).Click();session.FindElementByName(operation).Click();session.FindElementByName(input2).Click();session.FindElementByName("等于").Click();Assert.AreEqual(expectedResult, GetCalculatorResultText());}[ClassInitialize]public static void ClassInitialize(TestContext context){// Create session to launch a Calculator windowSetup(context);// Identify calculator mode by locating the headertry{header = session.FindElementByAccessibilityId("Header");}catch{header = session.FindElementByAccessibilityId("ContentPresenter");} // Ensure that calculator is in standard modeif (!header.Text.Equals("标准", StringComparison.OrdinalIgnoreCase)){session.FindElementByAccessibilityId("TogglePaneButton").Click();Thread.Sleep(TimeSpan.FromSeconds(1));var splitViewPane = session.FindElementByClassName("SplitViewPane");splitViewPane.FindElementByName("标准").Click();Thread.Sleep(TimeSpan.FromSeconds(1));Assert.IsTrue(header.Text.Equals("标准", StringComparison.OrdinalIgnoreCase));}// Locate the calculatorResult elementcalculatorResult = session.FindElementByAccessibilityId("CalculatorResults");Assert.IsNotNull(calculatorResult);}[ClassCleanup]public static void ClassCleanup(){TearDown();}[TestInitialize]public void Clear(){session.FindElementByName("清除").Click();Assert.AreEqual("0", GetCalculatorResultText());}private string GetCalculatorResultText(){return calculatorResult.Text.Replace("显示为 ", string.Empty).Trim();}}
}
winform程序类似,先获取元素,然后模拟点击,输入框的模拟输入即可。