Introduction
WinAppDriver was Microsoft’s answer to UI automation for Windows desktop apps. Built on the WebDriver protocol, it allowed developers to write UI tests using familiar tools and languages. However, WinAppDriver is no longer actively maintained, and its limitations—especially around flexibility and community support—have become more apparent over time.
Appium, a widely-used open-source automation framework, now offers a powerful alternative through its Windows driver. It supports the same WebDriver protocol but with more active development, better extensibility, and cross-platform capabilities. For teams looking to future-proof their automation, migrating from WinAppDriver to Appium is a practical and strategic move.
Why Migrate to Appium
Appium is a strong alternative to WinAppDriver, primarily because it’s actively maintained, unlike WinAppDriver, which hasn’t received updates in quite some time. With Appium, you get regular updates, bug fixes, and a thriving community that’s continuously improving the tool.
While Appium’s Windows driver still relies on WinAppDriver under the hood, it provides a more unified and extensible interface. The core functionality and commands—such as locator strategies and element interactions—are very similar to WinAppDriver, so most of your test scripts won’t need to be completely rewritten. Expect some slight syntax changes, like adjustments in method names and session handling, but the transition is relatively smooth.
What Appium adds is extra flexibility. It opens the door to additional Appium-specific commands, better session management, and the ability to use plugins to extend functionality. Plus, Appium integrates more easily into modern development environments, allowing you to install it via npm, script it, and run it in CI/CD pipelines, which is a significant improvement over WinAppDriver’s more rigid setup.
Migrating to Appium is also the first step if you plan to try out our new Appium driver, NovaWindows. We designed it to provide faster and more reliable Windows desktop automation and integrate seamlessly with Appium. So by migrating to Appium first, you’re setting yourself up to easily adopt NovaWindows when you’re ready for it.
Common Prerequisites
In order to start using Appium for desktop automation, first you need to have Node.js installed on your system, as Appium is Node.js based. After you do, you can install appium by running the following command:
npm install -g appium
This will install the Appium package globally on your system. This way you can run the Appium command from anywhere. Before we run it though, we need to install the Appium Windows driver. Starting from version 2, Appium no longer bundles the drivers with itself, but they have to be installed externally, usually with a simple command. To download the Appium interface for WinAppDriver in appium, you run the following command:
appium driver install --source=npm appium-novawindows-driver
You can see the list of other available drivers here: https://appium.io/docs/en/latest/ecosystem/drivers/, including our new NovaWindows driver, that you might want to try after fully migrating to Appium.
After the install is done, simply run the command appium anywhere, and the server should be up and listening for requests.
Migration in JavaScript (WebDriverIO)
If you’re using WebDriverIO, you’re probably using winappdriver service for it. When migrating to Appium, you’ll need to swap it to an appium service. You can do it by running:
npm install @wdio/appium-service --save-dev
The next important thing in changing the capabilities. All non-standard Selenium capabilities have to have the appium: prefix. This is how they should look like inside wdio.conf.js:
...
capabilities: [{
platformName: 'Windows',
'appium:app': 'C:\\Path\\To\\App.exe',
'appium:automationName': 'Windows'
}],
...
WebDriverIO’s framework provides a unified interface for all automation targets, so your locators and methods should remain unchanged.
Migrating in Python
The changes required in Python are relatively simple. Since WinAppDriver uses the outdated Selenium 3 protocol, and Appium uses Selenium 4, we need to upgrade the client libraries. You can do so by running the following command:
pip install --upgrade Appium-Python-Client selenium
This will update Appium and Selenium clients to the latest versions. You may start seeing a lot of errors in the code. Don’t worry, the changes are quite simple. First, the capabilities. In Selenium 3, the desired capabilities are deprecated in favor of Options. For desktop, you will need to import WindowsOptions, and initialize WindowsOptions object, instead of simple dictionary.
Instead of initializing capabilities like this:
desired_caps = {
'platformName': 'Windows',
'app': 'C:\\Path\\To\\App.exe'
}
You initialize options like this:
from appium.options.windows import WindowsOptions
opts = WindowsOptions()
opts.app = 'C:\\Path\\To\\App.exe'
# for additional capabilities
opts.set_capability('namespace:key', 'value')
Before:
driver.find_element_by_accessibility_id('AppNameTitle')
driver.find_element_by_class_name('TextBlock')
driver.find_element_by_id('42.333896.3.1')
driver.find_element_by_name('Calculator')
driver.find_element_by_tag_name('Text')
driver.find_element_by_xpath('(//Button)[2]')
After:
from appium.webdriver.common.appiumby import AppiumBy
driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='AppNameTitle')
driver.find_element(by=AppiumBy.CLASS_NAME, value='TextBlock')
driver.find_element(by=AppiumBy.ID, value='42.333896.3.1')
driver.find_element(by=AppiumBy.NAME, value='Calculator')
driver.find_element(by=AppiumBy.TAG_NAME, value='Text')
driver.find_element(by=AppiumBy.XPATH, value='(//Button)[2]')
That’s it, you should be good to go, as everything else should be the same!
Migrating in Java
If you’re using WinAppDriver with Java, you’re probably using io.appium.java_client version 7.6.0, since the newer versions are using the updated Selenium 4 protocol, which is incompatible with WinAppDriver. That’s the first thing we have to upgrade when migrating to Appium.
After the changes, there will be some changes in the code that we have to do, starting with initializing the driver and capabilities. Currently, the capabilities probably look something like this:
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("platformName", "Windows");
capabilities.setCapability("app", "C:\\Path\\To\\App.exe");
This won’t work anymore in the new Appium, since it requires “app” and other appium-specific capabilities to be prefixed with namespace “appium:”. You don’t have to do it manually though, there’s a better way to initialize capabilities, as WindowsOptions:
WindowsOptions options = new WindowsOptions();
options.setApp("C:\\Path\\To\\App.exe");
// for additional capabilities
options.setCapability("namespace:key", "value")
As for the driver, it no longer requires generic parameter, as WindowsElement no longer exists in the new version of the client. Next thing is the element locators. You may have a lot of maps, but you should be able to fix everything with a mass replace. Everything that has been WindowsElement will now be of class WebElement. Here are the rest of the changes:
Before:
driver.findElementByAccessibilityId("AppNameTitle");
driver.findElementByClassName("TextBlock");
driver.findElementById("42.333896.3.1");
driver.findElementByName("Calculator");
driver.findElementByTagName("Text");
driver.findElementByXPath("(//Button)[2]");
After:
import io.appium.java_client.AppiumBy;
driver.findElement(AppiumBy.accessibilityId("AppNameTitle"));
driver.findElement(AppiumBy.className("TextBlock"));
driver.findElement(AppiumBy.id("42.333896.3.1"));
driver.findElement(AppiumBy.name("Calculator"));
driver.findElement(AppiumBy.tagName("Text"));
driver.findElement(AppiumBy.xpath("(//Button)[2]"));
If there any other errors regarding some windows-specific methods that no longer exists, you may have to use the executeScript method, consult the appium-windows-driver repo’s readme for documentation of the platform-specific commands.
Migrating in Ruby
If you’re running WinAppDriver in Ruby, you’re in luck! You probably have to make the least changes to your code, especially if you’re on the latest appium_lib version! Just add the automationName property to your capabilities and you should be good to go!
...
automationName: 'Windows',
...
Migrating in .NET
If you’re using WinAppDriver with C#, you’re using one of the two packages—either Microsoft.WinAppDriver.Appium.WebDriver or Appium.WebDriver with version up to 4.4.5.
// Microsoft.WinAppDriver.Appium.WebDriver
var caps = new DesiredCapabilities();
caps.SetCapability("app", "C:\\Path\\To\\App.exe");
var driver = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), caps);
// Appium.WebDriver up to version 4
var opts = new AppiumOptions();
opts.AddAdditionalCapability("app", "C:\\Path\\To\\App.exe");
var driver = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), opts);
Either way, you will need to install the latest version of Appium.WebDriver and remove Microsoft.WinAppDriver.Appium.WebDriver if used. Just like in Java, the generic type is not needed anymore in WindowsDriver as WindowsElement no longer exists, it uses the AppiumElement. This is how you define the new AppiumOptions and WindowsDriver:
var opts = new AppiumOptions
{
PlatformName = "Windows",
AutomationName = "Windows",
App = "C:\\Path\\To\\App.exe",
};
var driver = new WindowsDriver(new Uri("http://127.0.0.1:4723"), caps);
Next thing is the element locators. You may have a lot of maps, but you should be able to fix everything with a mass replace. Everything that has been WindowsElement will now be of type AppiumElement. Here are the rest of the changes:
Before:
driver.FindElementByAccessibilityId("AppNameTitle");
driver.FindElementByClassName("TextBlock");
driver.FindElementById("42.333896.3.1");
driver.FindElementByName("Calculator");
driver.FindElementByTagName("Text");
driver.FindElementByXPath("(//Button)[2]");
After:
driver.FindElement(MobileBy.AccessibilityId("AppNameTitle"));
driver.FindElement(MobileBy.ClassName("TextBlock"));
driver.FindElement(MobileBy.Id("42.333896.3.1"));
driver.FindElement(MobileBy.Name("Calculator"));
driver.FindElement(MobileBy.TagName("Text"));
driver.FindElement(MobileBy.XPath("(//Button)[2]"));
If there any other errors regarding some windows-specific methods that no longer exists, you may have to use the executeScript method, consult the appium-windows-driver repo’s readme for documentation of the platform-specific commands.
