WinAppDriver: Windows Desktop App Testing with WebDriver
Windows desktop applications — whether built with WPF, WinForms, or Win32 — require specialized automation tooling. WinAppDriver (Windows Application Driver) is Microsoft's open-source tool that implements the WebDriver protocol for Windows UIA (UI Automation), letting you automate Windows applications using the same Appium-style API used for mobile testing.
What WinAppDriver Does
WinAppDriver acts as a bridge between your test code and Windows' UI Automation framework. It:
- Runs as a local HTTP server on port 4723
- Accepts WebDriver/Appium commands from your tests
- Translates them to Windows UI Automation API calls
- Returns results back to your test code
This means you can write tests in any language with an Appium client library (C#, Java, Python, JavaScript) and they work against any Windows application.
Installation
Prerequisites:
- Windows 10 or 11
- Developer Mode enabled (
Settings > Update & Security > For developers > Developer mode)
Install WinAppDriver:
Download from GitHub releases: microsoft/WinAppDriver
Or via winget:
winget install Microsoft.WinAppDriverStart the server:
# Default — listens on localhost:4723
"C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe"
# With specific address and verbose logging
WinAppDriver.exe 127.0.0.1 4723 /wd/hub 1Writing Tests in C#
The standard language choice for Windows testing is C#, using Appium.WebDriver:
<!-- NuGet packages -->
<PackageReference Include="Appium.WebDriver" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="3.14.0" />using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using System;
[TestFixture]
public class NotepadTests
{
private WindowsDriver<WindowsElement> _driver;
private const string WinAppDriverUrl = "http://127.0.0.1:4723";
[SetUp]
public void Setup()
{
var options = new AppiumOptions();
options.AddAdditionalCapability("app", @"C:\Windows\System32\notepad.exe");
options.AddAdditionalCapability("deviceName", "WindowsPC");
_driver = new WindowsDriver<WindowsElement>(
new Uri(WinAppDriverUrl), options
);
_driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
}
[TearDown]
public void Teardown()
{
_driver?.Quit();
}
[Test]
public void TypeAndVerifyText()
{
var editArea = _driver.FindElementByClassName("Edit");
editArea.SendKeys("Hello, WinAppDriver!");
var text = editArea.Text;
Assert.That(text, Is.EqualTo("Hello, WinAppDriver!"));
}
[Test]
public void FileMenuExists()
{
var fileMenu = _driver.FindElementByName("File");
Assert.That(fileMenu.Displayed, Is.True);
fileMenu.Click();
var newMenuItem = _driver.FindElementByName("New");
Assert.That(newMenuItem.Displayed, Is.True);
}
}Finding Elements
UI Automation exposes elements differently than HTML DOM. The most reliable locators are:
// By Automation ID (set by developers in the app)
driver.FindElementByAccessibilityId("MyButtonAutomationId");
// By Name (text label)
driver.FindElementByName("Save");
driver.FindElementByName("File");
// By Class Name (Windows control class)
driver.FindElementByClassName("Button");
driver.FindElementByClassName("TextBox");
driver.FindElementByClassName("ListBox");
// By XPath (most flexible, slowest)
driver.FindElementByXPath("//Button[@Name='OK']");
driver.FindElementByXPath("//Edit[@AutomationId='usernameField']");
// Tag Name (mapped to UIA control type)
driver.FindElementByTagName("Button");Automation IDs are the gold standard. Ask your development team to add AutomationProperties.AutomationId to key UI elements in WPF/XAML:
<Button AutomationProperties.AutomationId="SaveButton" Content="Save" />
<TextBox AutomationProperties.AutomationId="EmailInput" />Using Inspect.exe to Find Elements
Windows SDK includes Inspect.exe — hover over any UI element to see its Automation properties:
Tool location: C:\Program Files (x86)\Windows Kits\10\bin\10.0.xxxxx.0\x64\inspect.exeInspect shows:
- Name — the visible label
- AutomationId — the programmatic ID
- ClassName — the control type
- ControlType — UIA control type (Button, Edit, etc.)
Testing WPF Applications
WPF applications expose rich UI Automation trees. A typical test flow:
[TestFixture]
public class CustomerManagementTests
{
private WindowsDriver<WindowsElement> _driver;
[SetUp]
public void Setup()
{
var options = new AppiumOptions();
options.AddAdditionalCapability("app",
@"C:\MyApp\CustomerManagement.exe");
_driver = new WindowsDriver<WindowsElement>(
new Uri("http://127.0.0.1:4723"), options
);
}
[Test]
public void CreateNewCustomer()
{
// Click "New Customer" button
_driver.FindElementByAccessibilityId("NewCustomerButton").Click();
// Fill in the form
_driver.FindElementByAccessibilityId("FirstNameInput").SendKeys("Alice");
_driver.FindElementByAccessibilityId("LastNameInput").SendKeys("Smith");
_driver.FindElementByAccessibilityId("EmailInput")
.SendKeys("alice@example.com");
// Submit
_driver.FindElementByAccessibilityId("SaveCustomerButton").Click();
// Verify the customer appears in the list
var customerList = _driver.FindElementByAccessibilityId("CustomerList");
var newCustomer = customerList.FindElementByName("Alice Smith");
Assert.That(newCustomer, Is.Not.Null);
}
}Session Management for Multiple Windows
When your app opens new windows:
// Get all window handles
var handles = _driver.WindowHandles;
// Switch to a specific window
_driver.SwitchTo().Window(handles[1]);
// Or wait for a new window to appear
var initialHandles = new HashSet<string>(_driver.WindowHandles);
_driver.FindElementByName("Open Dialog").Click();
WebDriverWait wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.WindowHandles.Count > initialHandles.Count);
var newHandle = _driver.WindowHandles
.FirstOrDefault(h => !initialHandles.Contains(h));
_driver.SwitchTo().Window(newHandle);Attaching to a Running Application
Instead of launching the app from WinAppDriver, attach to an already-running process:
// Get the process ID
var process = System.Diagnostics.Process.GetProcessesByName("MyApp").First();
var options = new AppiumOptions();
options.AddAdditionalCapability("appTopLevelWindow",
process.MainWindowHandle.ToString("x")); // hex window handle
_driver = new WindowsDriver<WindowsElement>(
new Uri("http://127.0.0.1:4723"), options
);This is useful when your app has a complex startup sequence that's easier to handle outside of tests.
Running in CI
WinAppDriver requires Windows — it won't run on Linux or macOS. GitHub Actions provides Windows runners:
# .github/workflows/windows-tests.yml
jobs:
desktop-tests:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Enable Developer Mode
run: |
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1"
- name: Start WinAppDriver
run: |
Start-Process "C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe"
Start-Sleep 3
shell: pwsh
- name: Run tests
run: dotnet test tests/DesktopTests.csprojLimitations and Alternatives
WinAppDriver has known limitations:
- No support for DirectX/OpenGL rendered content
- XPath queries can be slow on complex UIs
- Some WinForms controls expose limited accessibility data
Alternatives for specific scenarios:
- Playwright — for Electron or Chrome-based desktop apps
- FlaUI — open-source .NET library wrapping UI Automation directly, faster and more flexible
- Ranorex / TestComplete — commercial tools with more features
Monitoring Desktop App Services
WinAppDriver tests verify the UI. For monitoring the server-side services your Windows desktop app depends on, HelpMeTest provides 24/7 API health monitoring. If your desktop app's backend goes down, you know before users call support.
Summary
WinAppDriver brings WebDriver automation to Windows native apps:
- Works with any Windows application — WPF, WinForms, Win32, UWP
- Uses Windows UI Automation, which reads the actual accessibility tree
- AutomationId locators are most reliable — request them from developers
- Inspect.exe reveals what WinAppDriver can see in your app
- Runs on any Windows with Developer Mode enabled
- CI support via GitHub Actions Windows runners
For teams already familiar with Selenium or Appium, WinAppDriver requires minimal conceptual adjustment — the command vocabulary is identical.