Showing posts with label WinAppDriver. Show all posts
Showing posts with label WinAppDriver. Show all posts

Saturday, August 22, 2026

WinAppDriver Locators: Perils, Playbook, and AI Help

While the world has generally moved to mobile devices, desktop computers and applications still play a key role inside offices doing business. This is true for SMEs and enterprise alike. That's why the need for efficient test automation of all sorts of desktop applications, whether they work on Mac or Windows, has not gone away, it has just gone quiet. Nobody writes hype posts about desktop automation anymore, but somewhere behind almost every insurance company, bank, hospital, and manufacturing floor, a WPF, WinForms, or Win32 app is still running the actual business, and someone still has to test it.



For Windows apps specifically, WinAppDriver has been the default place teams start since Coded UI was retired. Worth saying up front, WinAppDriver itself has not seen much active development in a few years, releases have slowed and the open issue list is long, but it stays the practical starting point simply because there are not many real alternatives for driving native Windows UI. It is free, it is Microsoft's own tool, and it speaks the same WebDriver protocol Selenium and Appium already use, so C#, Java, and Python testers all feel at home with it fairly fast. What nobody tells you on day one is that the actual skill in WinAppDriver automation is not the driver setup, it is locators. Get those right and a suite runs for years. Get them wrong and you spend more time fixing "element not found" than writing new tests.

This post is about that exact skill: finding, choosing, and maintaining locators for WinAppDriver automation, plus a look at how AI and the Model Context Protocol (MCP) are starting to change how that discovery work gets done.

The Five Locator Strategies WinAppDriver Gives You

WinAppDriver, being a WebDriver-protocol server sitting on top of Windows UI Automation (UIA), exposes five ways to find an element:

  • Accessibility ID, matching the control's AutomationId. This is the one you want most of the time.
  • Name, matching the control's visible Name property.
  • Class Name, matching the control's ClassName (for example TextBlock or Button).
  • ID, matching RuntimeId, a value Windows assigns to a control at runtime.
  • XPath, which can match on any attribute at all.

That list looks flat on paper, but the strategies are not equally reliable, and the gap between them is where most WinAppDriver flakiness comes from.

Accessibility ID is the strategy to build your suite on. It is developer-set, stable across app restarts, and independent of language or window layout. If a screen has good AutomationIds, use them, full stop.

Name breaks the moment your app is localized, or even the moment a product manager tweaks copy from "Save" to "Save Changes." Fine as a fallback, risky as a primary strategy for anything beyond a quick script.

RuntimeId is a trap many people fall into once, and only once. It looks like a stable identifier because it is a specific-looking string, but it is assigned fresh by Windows each time the control is created, so it can, and often does, change between app restarts. Do not build locators on it.

XPath is the one to reach for last, and to use sparingly, for two reasons. First, an XPath query in WinAppDriver has to walk the full UIA tree with no indexing, which real-world reports on the WinAppDriver GitHub issue tracker describe as dramatically slower than a direct AccessibilityId or Name lookup, sometimes the difference between a near-instant find and a multi-second one. Second, an XPath tied to sibling position or index is exactly the kind of structural locator that breaks the moment a designer adds a new control to a panel. If you must use XPath, anchor it to a stable ancestor with a known AutomationId, and keep the path as short as you can. (There is a real exception to "use it sparingly," covered just below, if your project runs on Appium 2.0 or newer.)

A Real Gotcha: Appium 2.0+ Changes the Rules

Everything above assumes the classic setup: WinAppDriver.exe running as its own server, with your test project talking to it directly through a Selenium-style WebDriver client. A lot of what is out there still teaches exactly that.



If your project instead runs on Appium 2.0 or newer with the appium-windows-driver plugin, so an Appium server sits in front of WinAppDriver rather than your tests talking to it directly, watch out for a real, documented quirk: By.Id and By.Name locators can get translated into CSS selectors under the hood and simply fail to find anything. In that specific setup, XPath stops being a last resort and becomes the strategy that actually works.

I put together a full, working example of this exact Appium 5 plus WinAppDriver setup in C#, including the AppiumServiceBuilder startup pattern and a small EasyXPath helper class for building the XPath locators this setup needs, on GitHub: Appium5-WinAppDriver. Worth cloning and running side by side with whatever setup you are on, so you know which locator rules actually apply to your project before you build fifty tests on the wrong assumption.

Why WinAppDriver Locators Get Fragile in the First Place

A few realities make Windows desktop locators harder than the web locators most testers cut their teeth on, and they show up constantly in WinAppDriver suites specifically.

A lot of apps simply never set AutomationIds. Setting an AutomationId is a deliberate developer choice, and in older or legacy Win32, MFC, and even plenty of WPF codebases, nobody ever did it. You end up locating by Name, which is fine until localization or a copy change breaks half the suite in one release.

Custom-drawn controls can be invisible to UIA entirely. Canvas-based UI, some CAD and design tools, and heavily custom-rendered WPF controls can expose almost nothing to the accessibility tree WinAppDriver relies on. When that happens, none of the five strategies above help, and coordinate-based clicks become the only fallback, which is the most fragile option there is.

Coordinates and window layout drift. A locator anchored to screen position breaks the moment DPI scaling, resolution, or window size changes. This is common enough on shared CI machines that it deserves its own line: if your test farm runs at a different resolution than your dev machine, coordinate-based clicks are a ticking clock, not a strategy.

Timing is part of the locator problem, not a separate one. A control can exist in the UIA tree and still not be safe to interact with, disabled, off-screen, or sitting behind a modal dialog that has not finished rendering. WinAppDriver will happily hand you a reference to an element you cannot actually click yet.

A WinAppDriver Locator Playbook

Inspect before you write a single locator. WinAppDriver ships a bundled UI Recorder specifically for this, point it at your app and it shows you the AutomationId, Name, and ClassName for anything you click. The Windows SDK's Inspect.exe does the same job with a bit more detail on the full ancestor chain. Five minutes here saves an hour of trial and error later.



Centralize your locators in Page Objects. Whatever language you're driving WinAppDriver from, keep every screen's locators in one class or module, not copy-pasted string literals scattered across test methods. When an AutomationId changes, you want to fix it in one place, not grep your whole repo.

Search relative to a container, not the whole window. Finding a button by searching the entire application window is slower and more ambiguous than first finding the containing dialog or panel by its AutomationId, then searching within that. It is also safer if two screens in the same app happen to reuse a button labeled "OK."

Push for AutomationIds from the dev side. If a screen has none, that is not a testing problem to quietly route around forever, it is a product defect, the same category as a missing API endpoint. Teams that add AutomationIds in XAML or code from day one save their QA function months of pain over an app's life.

Treat your RuntimeId and coordinate-based locators as expiring debt. If you have to fall back to either while a screen lacks proper accessibility support, flag it, track it, and replace it once the real AutomationId lands. Do not let it quietly become permanent.

FlaUI as a WinAppDriver Sidekick

You do not need to choose between WinAppDriver and FlaUI, the open source .NET library built on Microsoft's UIA APIs. Plenty of teams run WinAppDriver as their actual test driver and lean on FlaUI, or a small standalone FlaUI script, purely as an inspection tool during locator discovery. Because FlaUI runs in-process rather than through a separate driver, it is quick to spin up a throwaway console app that walks a window's tree, prints every control's AutomationId, ClassName, and Name, and lets you confirm exactly what to plug into your WinAppDriver FindElementByAccessibilityId call, without touching Inspect.exe at all.

If you want to go deep on the WinAppDriver side specifically, from setup through real, running C# tests, that is exactly what my course, Windows Desktop Applications Test Automation in WinAppDriver, covers end to end. I also wrote a full breakdown of how WinAppDriver compares to the now-retired Coded UI if you want the background on why WinAppDriver became the default choice in the first place.

Waiting for Controls to Actually Show Up

A locator that finds the wrong moment in time is just as broken as one that finds the wrong element. Since WinAppDriver speaks the WebDriver protocol, the same explicit-wait pattern Selenium and Appium testers already know applies directly:

var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
var saveButton = wait.Until(d => d.FindElementByAccessibilityId("btnSave"));

That poll-until-true-or-timeout pattern, not a fixed sleep, is what keeps a suite fast on a quick CI machine and still reliable on a slow one. A few habits matter more than which exact wait class you reach for:

  • Wait on a condition, not a clock. "Wait until this element exists and is enabled" survives a loaded CI box. "Wait three seconds" does not.
  • For test steps that involve more than one flaky element, or that depend on a slow backend the app talks to, wrap the whole step in a retry policy with backoff rather than one fixed wait. Polly is the standard .NET library for this and composes cleanly around any WinAppDriver call.
  • If you are also using FlaUI for inspection or verification steps alongside WinAppDriver, its own Retry helper (Retry.WhileNull, Retry.WhileFalse, Retry.WhileException, each with a timeout and polling interval) is worth knowing, since it is purpose-built for exactly this kind of polling.
  • Wait for interactability, not just existence. WinAppDriver can hand you a reference to a control that is disabled or off-screen. Check the element's enabled state before you act on it, not just that the find call did not throw.

AI and MCP: Finding WinAppDriver Locators Faster

The most useful recent shift here is not a new locator syntax, it is tooling that lets an AI agent read the accessibility tree directly and reason about it, instead of guessing from a screenshot the way early "AI testing" tools did, and then hand you back exactly the AutomationId or path you plug into a WinAppDriver By call.

FlaUI-MCP, an open source MCP server built on FlaUI by Scott Hanselman, is a clean example. It exposes tools like windows_snapshot, which returns the live accessibility tree as structured element references, so an AI coding assistant can walk the real UIA tree, the same tree WinAppDriver itself queries under the hood, and hand back accurate AutomationIds, ClassNames, and ancestor paths for the controls you care about. Point it at your app during locator discovery, and it can shortcut a lot of manual Inspect.exe spelunking before you write the actual WinAppDriver test. Similar projects like windows-mcp-server extend this further with synthetic input and process control on top of the same accessibility-tree-first approach.

For scenarios no MCP server covers well yet, custom-written tree walker code is still a valid, sometimes necessary, fallback. Windows exposes System.Windows.Automation.TreeWalker directly in .NET, letting you write a small utility that recursively crawls a window's UIA tree and dumps every control's properties to JSON, then hand that JSON to an LLM with a prompt like "which of these elements is most likely the login button," and get back a suggested AutomationId or path to verify. It is a lighter-weight, roll-your-own version of what FlaUI-MCP does out of the box.

Two cautions matter more as this tooling gets more impressive. First, an AI suggesting a locator is not the same as an AI verifying one, always confirm a suggested AutomationId against a real inspector tool or an actual WinAppDriver test run before it lands in your suite; a language model can produce a plausible-looking but entirely wrong selector with total confidence. Second, an AI-suggested locator still belongs in your centralized Page Object, not pasted inline into a test method, or you have just automated your way into the same brittle mess faster.

Things You Must Never Do With WinAppDriver Automation

A short, hard-earned list:

  • Never rely on Thread.Sleep as your primary wait strategy. It is either too short and flaky, or too long and slow, never both correct and fast. Use WebDriverWait every time.
  • Never build a locator on RuntimeId. It looks stable and usually is not, it can change on every app restart.
  • Never automate purely by screen coordinates when a real locator is available. Coordinates break on the first DPI or resolution change, and they break silently, the click just lands on the wrong thing.
  • Never scatter the same locator string across dozens of test files. Centralize in Page Objects so a UI change costs one edit, not a search-and-replace across the whole suite.
  • Never trust visible Name text as your primary locator in an app that ships in more than one language. What is stable in English can vanish entirely in a localized build.
  • Never assume your suite can run against a locked or logged-off machine. WinAppDriver needs a real, interactive, unlocked session to drive UI at all. A CI runner that locks the screen on idle will fail your whole suite for reasons that have nothing to do with your app.
  • Never leave WinAppDriver.exe or the app under test running after a failed session. A crashed test that skips driver.Quit() leaves orphaned processes behind that steal focus and intercept clicks meant for the next run. Clean up in a finally block, every time, even on failure.
  • Never wrap a flaky locator in more retries without asking why it is flaky. Sometimes it is hiding a real race condition or a bug in the app itself. Blind retries just turn a real defect into an intermittent one nobody ever files.
  • Never skip explicit handling for modal dialogs. "Element not found" is, more often than not, a focus problem in disguise, a dialog stole input focus and your script kept looking at the window behind it.
  • Never treat a missing AutomationId as purely your problem to work around. It is a testability gap in the product. Raise it, do not just quietly build a fragile workaround and move on.

Wrapping Up

WinAppDriver gives you five ways to find an element, but only one of them, AccessibilityId, deserves to be your default. Everything else in a stable suite follows from that one fact: centralize locators so a UI change costs one edit, wait on conditions instead of clocks, and treat a missing AutomationId as a product defect rather than a personal problem to route around before a release.

What has genuinely changed recently is how fast the discovery part of that work goes. Tools like FlaUI-MCP put a real accessibility tree in front of an AI agent instead of a screenshot, which means the tedious part, figuring out what a control is actually called under the hood, is finally starting to get faster without getting less accurate, and the AutomationId it hands you drops straight into a WinAppDriver test.

If you want to see real, working code first, my Appium5-WinAppDriver repo on GitHub is free and shows the full Appium 5 plus WinAppDriver setup in C#, XPath helper class included.

If you want the full setup explained end to end, from installing WinAppDriver through writing real, running C# tests against a live desktop app, that is exactly what my course walks through.

Windows Desktop Applications Test Automation in WinAppDriver (code CBP2026AU) →

Friday, May 15, 2020

WinAppDriver Tutorial: Using Appium Client For Object Inspection in Windows Desktop Test Automation

Appium Server Windows with Free Test Automation Tool WinAppDriver Object Inspector


Download free e-book Windows Test Automation With Appium Windows Application Driver in C# .Net


Introduction to Appium Desktop Client


When I created my Udemy Course about WinAppDriverUI Automation Testing in 2019, I relied solely on the WinAppDriver UI Recorder to inspect various Windows application elements. I showed how to automate. Only a little information was available on the subject back then. I recently came across another tool we can use to look into various application elements and find their attributes for UI test automation.

This article shows you just that because it has the potential to make your life easier. I've also added the same videos to my Udemy course and YouTube channel. This tool is called the Appium Desktop.  

This post will serve as an introductory WinAppDriver tutorial. I am going to show you how to use an alternative of
WinAppDriver UI Recorder.
You can download it from the Appium.io website. 

Before we proceed, please remember there are two prerequisites for using Appium Desktop. 
  1. Enable Windows 10 Developer Mode on your PC
  2. You need to have WinAppDriver 1.1 or above installed on your PC. 



    You don't need to install Node.js to run an Appium desktop client, as shown in this WinAppDriver tutorial. Let me google the search term Appium Desktop client quickly and guide you. You also don't need to download WinAppDriver UI Recorder.

    Download Appium Desktop on Windows

    This will take us to the GitHub repository of the Appium Desktop project. You will need to download the Appium-windows.exe from here and use it to install an Appium desktop.

    Please see the screenshot below.


    Appium Desktop Download Items
    Once you've got Appium Desktop installed, you can proceed further. We are going to start using Appium Desktop for inspecting Windows Applications. If you have the proper tools installed, please note that you can use the same utility to check apps on other Appium-supported platforms, such as Android and iOS. Although that's beyond the scope of this blog.



    Running Appium Desktop Client and Connecting to a Windows Application


    Before we use the Appium Desktop Client to inspect the elements of a Windows application's features, we must start the Windows Application Driver through WinAppDriver.exe


    Learn WinAppDriver Automation Testing Free Tools

    Now let's run the Appium Desktop Client; you can find it in your Windows applications. First, click the "Start" button and type Appium. 



    Appium Client in Windows Apps List

    Now click the "Appium" icon to open the application. The Appium desktop client can take a few seconds to load. Then, on the main Appium UI, go to the "File" menu and select "New Session Window." 

    Appium New Session on Windows

    A new dialog box will open up. The tab "Custom Server" will be selected. I'll leave the remote host and remote port fields unchanged. In the Remote Path field, I'm going to put a forward slash. Then there's this table-like Ui to specify Desired Capabilities. I will click on the first text box containing the "Name." I will type the word app in there. Please note the phrase app will be small. In the value textbox, I'll paste the user mode ID of the Microsoft Windows 10 Calculator app, which is:

    Microsoft.WindowsCalculator_8wekyb3d8bbwe!App


    I've shown how to find this value in my course. The final screenshot of the Appium custom server is shown below. FYI: 

    Setting AppiumOptions in Appium Client for Windows Automation Example



    Now Appium Client will connect the instance of WinAppDriver, which is running on this PC and listening on port number 4723. Remember, we started it earlier. I'll press the button "Start Session."

    Appium Client Inspecting Windows Calculator Example


    The calculator app will start, and you'll get a preview inside the Appium UI Recorder. You can hover the mouse on various application controls under the test, highlighted in yellow. In my case, the mouse cursor is also changing into a hand, but it was not captured in the screenshot.


    Appium UI inspector

    Let's end this section here, and we will learn more about the free Appium UI Inspector
    in the selection below. It is an essential tool for Appium-based automation.


    Inspecting Elements Using Appium Desktop Client and WinAppDriver For Automated Testing


    Once we have created a session in Appium Client, we can inspect various elements of the loaded application. The Appium Client object inspector UI is pretty simple. A button row is on the top, and "Select Elements" is clicked by default. 


    Appium Client Select Element Windows Automation
    1. We have the application preview loaded on the left side.
    2. "App Source" in the middle shows the hierarchy of application controls. 
    3. The rightmost panel is "Selected Element." It will populate when we click an element. 


    Appium Client Object Inspector Windows Selected Element




    For Example, we will hover the mouse on the application preview shown in the Appium Desktop Client. 


    You will see that the element under the cursor will highlight yellow. Let's click on button two in the application preview. You see, its color will change. In addition, you'll find tributes to this Windows Element will load in the right-hand pane. I've highlighted some properties/attributes (Name, AutomationId) that we can use for automation in the screenshot below.


    Windows WebElement Properties in Appium Client GUI Inspector

    The same thing will happen if we click the calculation results textbox on the top. 

    You can use the Appium desktop client to inspect Windows applications. However, other options on this screen could be more helpful. Instead, click on the given line to find out How to Download and Run WinAppDriver UI Recorder.

    Oxygen IDE Support: 


    Please note that you can record automated UI tests using Appium Desktop Inspector to run with Oxygen IDE. You'll need to have proper tools installed for that. This includes Android Studio, which is overhead, but so far, that's the way Oxygen IDE architecture is, and they don't have any plans to remove the dependency anytime soon. You can find more by visiting this link.

    You can now play with the inspector further to see more options. I am going to close it now. In the following video, I'll show you how to use the app arguments desired capability in Appium Desktop Client.

    app arguments Specifying Additional Desired Capabilities in
    Appium Desktop Client


    How to use appArguments AppiumOption/DesiredCapability in Appium? 

    We can specify more additional capabilities using the Appium Desktop Client. These capabilities can have various effects on our tests and are something not available in WinAppDriver UI Recorder. For Example, if we want to open Notepad with a file loaded into it already. Please note that you can't do the same thing in WinAppDriver UI Recorder software.

    Let us create a new session. In the app's default capability, I'll put "Notepad." Then I'll click the plus sign and add app arguments in the desired skills. Finally, I'll put the file's path we want to open in the value field.

    For Example, in my case, C:\Users\Naeem\Desktop\bunch of text.txt. Please note that we won't put double quotes around the path. The final screenshot of our new Appium client custom server to launch Windows 10 Notepad with a file opened inside of it is given below:


    How to configure Appium desktop client with WinAppDriver on Windows

    I'll click the "Start Session" button. You'll see that the file you specified as a parameter will open. 



    If you're doing Microsoft Excel automation, Microsoft Excel can use the same technique to open a file right out of the gates. You may need to test an Excel plugin with test automation using WinAppDriver(Free automation tool).


    Click on the link to find out "How to Download and Run WinAppDriver UI Recorder."
    It is also possible to open files in Microsoft Excel launched through WinAppDriver using appArguments Appium Option. 
    Specify the desired capabilities in your test automation code will be done through the method call listed below: 

    AppiumOptions.AddAdditionalCapability("appArguments", "c:\textfile.txt");
    AppiumOptions.AddAdditionalCapability("ms:waitForAppLaunch", "50");

    ms:waitForAppLaunch Handling Splash Screen in WinAppDriver


    How to wait for an application splash screen to display? For Example, how to use ms:waitForAppLaunch AppiumOption? 

    Some applications show a splash screen when they launch. Unfortunately, the splash is mistakenly considered the main application window by the WinAppDriver, leading to automation failure. 



    Microsoft Outlook Automation Testing with Appium WinAppDriver



    If you're using WinAppDriver 1.2 RC, you can also make the WinAppDriver wait a bit so that the splash goes away, and then the WinAppDriver attaches to the application window. 

    For Example, let's try a demo with Microsoft Outlook(2019), a Desktop e-mail client application. 

    A free code sample of Outlook 2019 automation using WinAppDriver can be found in your GitHub repository. I am going to launch it straightforwardly. Then, I'll create a new session window and put Outlook against the "app" in desired capabilities. 

    Now I'll click the "Start Session" button.


    Appium Client Object Inspector Loading Outlook for WinAppDriver Automation


    You'll see that the app preview will not load. The progress animation will keep spinning. Let's close the session window and come again. This time, I'm going to add a new Desired Capability. In the name field, I'll put ms:waitForAppLaunch in the value field, I'll put the number 15. 

    Now I'll click the "Start Session" button. 


    Windows Outlook Appium Client AppiumOptions Values

    This time, you'll see that the application preview will take a while to load in the Appium Desktop Client. This is because now you can check all Windows elements' properties inside this application. 
    Outlook Windows Mailing List Screenshot for Free Windows Automation Tool



    This is the end of this article, and I hope you found it helpful. Please feel free to ask questions.

    Do share it in your social circles as well. Please also visit my Udemy course; the link is given AppiumWinAppDriver UI Automation Testing.

    Hopefully, you found this lesson to be valuable. If you want me to write another WinAppDriver tutorial, just let me know. This article was updated in 2023.