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 Appium
private 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 launched
if (session == null)
{
// Create a new session to bring up an instance of the Calculator application
// Note: Multiple calculator windows (instances) share the same process Id
DesiredCapabilities 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 times
session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1.5);
}
}
public static void TearDown()
{
// Close the application and delete the session
if (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 = 8
session.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 = 8
session.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 = 8
session.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 results
session.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 window
Setup(context);
// Identify calculator mode by locating the header
try
{
header = session.FindElementByAccessibilityId("Header");
}
catch
{
header = session.FindElementByAccessibilityId("ContentPresenter");
}
// Ensure that calculator is in standard mode
if (!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 element
calculatorResult = 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程序类似,先获取元素,然后模拟点击,输入框的模拟输入即可。