This is the list of main changes between major versions 10 and 11 of Appium java client. This list should help you to successfully migrate your existing automated tests codebase.
The main theme of this release is that Appium java client depends on selenium-api only. The rest of
Selenium (selenium-remote-driver, selenium-http, selenium-json, selenium-os, selenium-support)
is not a dependency anymore. The parts of it that the client needs are now maintained in this project,
so changes in the browser oriented parts of Selenium cannot break Appium sessions anymore.
See Transitive dependencies management for details.
The minimum supported Appium server version is now Appium 3, and the deprecated APIs that were kept for the older servers are removed.
For Appium 3 and newer servers nothing changes on the wire: the requests sent to the server, the capabilities and the options classes, the driver classes and their methods, and the exceptions thrown for server errors are the same as in v10. What you need to adapt are the imports, the few places where your code relied on Selenium classes that the client does not extend or expose anymore, and the usages of the removed deprecated APIs.
- Update the client to
11.xand Selenium to4.50.0or newer. Selenium versions below 4.50.0 won't work with Appium java client 11+. Check the Compatibility Matrix for more details. Use Appium server 3 or newer and the drivers and plugins that are compatible with it, see the Appium server. - Declare the Selenium modules your own code uses. They are not brought by the client anymore:
selenium-support(WebDriverWait,ExpectedConditions,Select,EventFiringDecorator, ...),selenium-remote-driver(RemoteWebDriver,Augmenter, ...), browser drivers (ChromeOptions, ...). - Run the OpenRewrite recipe, it fixes most of the imports.
- Fix what is left of the compilation errors using the tables below.
- If you need
RemoteWebDriveror Selenium BiDi modules, add the optionalio.appium:java-client-selenium-bridgeartifact, see Selenium interoperability.
The recipe file is a declarative OpenRewrite
recipe. Run it before changing the client version, while the v10 types are still on the classpath.
Save the file as rewrite.yml in the root of your project and run:
Maven
mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
-Drewrite.activeRecipes=io.appium.java_client.MigrateToV11Gradle
plugins {
id 'org.openrewrite.rewrite' version '7.39.0'
}
rewrite {
activeRecipe('io.appium.java_client.MigrateToV11')
}./gradlew rewriteRun| Recipe | What it does |
|---|---|
io.appium.java_client.MigrateToV11 |
Moves the types listed in the first table to their new packages. |
io.appium.java_client.MigrateToV11PageObjectAnnotations |
Optional. Switches @FindBy, @FindBys, @FindAll, @CacheLookup and How to the Appium ones. Skip it if the same code also uses the Selenium PageFactory. |
io.appium.java_client.FindV11Breakages |
Marks the usages that have no automatic replacement (RemoteWebDriver, HasBiDi, BiDi, DriverService, touch actions, AppiumFunction, the mobile command helpers) with /*~~>*/. Search for MobileCommand yourself, as only some of its constants are removed. |
Several recipes can be activated at once, separated with commas. The recipe does not touch your build files
and does not rewrite casts to RemoteWebDriver. Review the result before committing it. If the same codebase
also automates browsers, check the changes of RemoteWebElement, as the recipe replaces every usage of it.
These types are covered by the recipe. The names stay the same, only the package changes.
| v10 (Selenium) | v11 |
|---|---|
org.openqa.selenium.remote.{SessionId, Response, Command, CommandPayload, DriverCommand, ExecuteMethod, CommandExecutor, ErrorHandler, ErrorCodes, ScreenshotException, UnreachableBrowserException} |
io.appium.java_client.remote.* |
org.openqa.selenium.remote.CapabilityType |
io.appium.java_client.remote.CapabilityType (PLATFORM_NAME and BROWSER_NAME only) |
org.openqa.selenium.remote.RemoteWebElement |
io.appium.java_client.remote.AppiumWebElement |
org.openqa.selenium.remote.HttpCommandExecutor |
io.appium.java_client.remote.AppiumCommandExecutor |
org.openqa.selenium.remote.CommandInfo |
io.appium.java_client.AppiumCommandInfo |
org.openqa.selenium.remote.http.{ClientConfig, HttpClient, HttpRequest, HttpResponse, HttpMethod, HttpHandler, HttpHeader, HttpMessage, Filter, Contents, WebSocket, Message, TextMessage, BinaryMessage, CloseMessage, ConnectionFailedException} |
io.appium.java_client.http.* |
org.openqa.selenium.support.{PageFactory} and org.openqa.selenium.support.pagefactory.{ElementLocator, ElementLocatorFactory, FieldDecorator, DefaultFieldDecorator, AbstractAnnotations, ByAll} |
io.appium.java_client.support.* and io.appium.java_client.support.pagefactory.* |
org.openqa.selenium.support.{FindBy, FindBys, FindAll, CacheLookup, How} (optional, the Selenium ones keep working while selenium-support is available) |
io.appium.java_client.support.* |
io.appium.java_client.functions.ExpectedCondition |
org.openqa.selenium.support.ui.ExpectedCondition |
| v10 | v11 |
|---|---|
AppiumDriver is a RemoteWebDriver; casts to it, Augmenter, RemoteWebDriver parameters |
AppiumDriver extends AppiumRemoteWebDriver. Type against WebDriver, or use SeleniumBridge.asRemoteWebDriver(driver) from the bridge. |
HasBiDi, driver.getBiDi(), driver.maybeGetBiDi() |
Removed. Use the Selenium BiDi modules through the bridge. The webSocketUrl capability of the session is still available. |
new ErrorHandler(codes, true) |
new ErrorHandler(codes) |
new AppiumDriver(HttpCommandExecutor, caps) and the same for the platform drivers |
new AppiumDriver(AppiumCommandExecutor, caps) |
new AndroidDriver(ClientConfig, caps), same for the other drivers |
new AndroidDriver(AppiumClientConfig, caps), create it with AppiumClientConfig.defaultConfig() or AppiumClientConfig.fromClientConfig(clientConfig) |
ClientConfig.defaultConfig() |
ClientConfig is an interface now, use AppiumClientConfig.defaultConfig() |
driver.getClientConfig() |
((AppiumCommandExecutor) driver.getCommandExecutor()).getAppiumClientConfig() |
AppiumCommandExecutor#setPrivateFieldValue, the public client field |
Removed, use getClient() |
AppiumDriverLocalService/AppiumServiceBuilder as DriverService/DriverService.Builder |
They are standalone classes now. getExecutable, setExecutable, sendOutputTo, getDriverProperty, getDriverEnvironmentVariable, score and withLogOutput are removed. |
TouchAction, MultiTouchAction, PerformsTouchActions, AndroidTouchAction, IOSTouchAction, io.appium.java_client.touch.*, functions.ActionSupplier |
Removed. Use W3C Actions or the mobile: gesture commands. |
PageFactory.initElements(driver, page) (the shortcut that takes a WebDriver) |
Removed. Use PageFactory.initElements(new AppiumFieldDecorator(driver), page). |
Wait/FluentWait/Sleeper of Selenium used with AppiumFluentWait |
AppiumFluentWait extends io.appium.java_client.support.ui.FluentWait. Change the declared type to AppiumFluentWait (or var), or to io.appium.java_client.support.ui.Wait. |
AppiumFunction, MobileCommand.prepareArguments |
Removed. Use java.util.function.Function and a Map. Function#compose and Function#andThen pass null on, while the AppiumFunction ones skipped the next step, so the steps of a FluentWait chain must handle null themselves. |
AndroidMobileCommandHelper, IOSMobileCommandHelper, the deprecated MobileCommand constants and *Command methods |
Removed. Send the mobile: extension with executeScript, see the removed commands. |
CanRememberExtensionPresence, AppiumDriver#assertExtensionExists, AppiumDriver#markExtensionAbsence |
Removed, the fallbacks that needed them are removed. |
AppiumProtocolHandshake, AppiumNewSessionCommandPayload |
Removed |
RemoteWebDriver and RemoteWebElement features that Appium servers do not serve: downloads, file upload and FileDetector, web storage, DevTools, tracing, fireSessionEvent, setLogLevel, RemoteWebDriver.builder() |
Not available. Shadow roots, virtual authenticators, federated credential management and manage().logs() keep working. |
- The minimum supported Appium server version is Appium 3. Only the drivers and plugins that are compatible with Appium 3 or newer are supported.
- A major release of the client guarantees support only for the Appium server API of the major server version that is current at the time of the release and of the previous major version. Endpoints and commands that were removed or deprecated in the server earlier may not be supported anymore. Check the Appium 2 to 3 migration guide for the endpoints that Appium 3 removed.
The driver interfaces (InteractsWithApps, LocksDevice, PullsFiles, PushesFiles, HidesKeyboard,
HasClipboard, PressesKey, StartsActivity, ...) used to call the legacy command when the server did not have the
mobile: extension. They call the mobile: extensions only now, so driver.installApp(...), driver.lockDevice()
and the other API methods need no change, but UnsupportedCommandException and InvalidArgumentException of the
server are not swallowed anymore. If your code sends the legacy commands with MobileCommand, switch to the extensions.
The arguments of the extensions differ from the ones of the legacy commands, check the documentation of the drivers.
Removed (MobileCommand constants and *Command helpers) |
Use instead |
|---|---|
RESET |
mobile: clearApp, then mobile: activateApp |
LAUNCH_APP, CLOSE_APP |
mobile: activateApp or mobile: launchApp (iOS), mobile: terminateApp |
RUN_APP_IN_BACKGROUND, GET_STRINGS |
mobile: backgroundApp, mobile: getAppStrings |
IS_APP_INSTALLED, INSTALL_APP, ACTIVATE_APP, QUERY_APP_STATE, TERMINATE_APP, REMOVE_APP |
mobile: isAppInstalled, installApp, activateApp, queryAppState, terminateApp, removeApp |
GET_CLIPBOARD, SET_CLIPBOARD |
mobile: getClipboard, mobile: setClipboard |
HIDE_KEYBOARD, IS_KEYBOARD_SHOWN, hideKeyboardCommand, isKeyboardShownCommand |
mobile: hideKeyboard, mobile: isKeyboardShown |
LOCK, UNLOCK, IS_LOCKED, lockDeviceCommand, unlockDeviceCommand, getIsDeviceLockedCommand |
mobile: lock, mobile: unlock, mobile: isLocked |
GET_DEVICE_TIME |
mobile: getDeviceTime |
pushFileCommand |
mobile: pushFile. PULL_FILE, PULL_FOLDER and PUSH_FILE stay, they are public now. |
SHAKE, TOUCH_ID, TOUCH_ID_ENROLLMENT (iOS) |
mobile: shake, mobile: sendBiometricMatch, mobile: enrollBiometric |
PRESS_KEY_CODE, LONG_PRESS_KEY_CODE, pressKeyCodeCommand, longPressKeyCodeCommand (Android) |
mobile: pressKey |
CURRENT_ACTIVITY, GET_CURRENT_PACKAGE, START_ACTIVITY (Android) |
mobile: getCurrentActivity, mobile: getCurrentPackage, mobile: startActivity |
GET_DISPLAY_DENSITY, GET_SYSTEM_BARS, OPEN_NOTIFICATIONS (Android) |
mobile: getDisplayDensity, mobile: getSystemBars, mobile: openNotifications |
GET_NETWORK_CONNECTION, SET_NETWORK_CONNECTION, TOGGLE_WIFI, TOGGLE_DATA, TOGGLE_AIRPLANE_MODE (Android) |
mobile: getConnectivity, mobile: setConnectivity |
TOGGLE_LOCATION_SERVICES, FINGER_PRINT (Android) |
mobile: toggleGps, mobile: fingerprint |
SEND_SMS, GSM_CALL, GSM_SIGNAL, GSM_VOICE, NETWORK_SPEED, POWER_CAPACITY, POWER_AC_STATE (Android emulator) |
mobile: sendSms, gsmCall, gsmSignal, gsmVoice, networkSpeed, powerCapacity, powerAc |
GET_PERFORMANCE_DATA, GET_SUPPORTED_PERFORMANCE_DATA_TYPES (Android) |
mobile: getPerformanceData, mobile: getPerformanceDataTypes |
SET_VALUE, REPLACE_VALUE (Android) |
W3C sendKeys, mobile: replaceElementValue |
END_TEST_COVERAGE |
No replacement, mobile: shell can run the instrumentation command |
setPowerAC requested mobile: powerAC, which the driver does not register (it is mobile: powerAc), so it always used
the legacy command. It requests the right extension now.
WindowsDriver#launchApp and WindowsDriver#closeApp send windows: launchApp and windows: closeApp. The Windows driver
has no extensions to transfer files, so WindowsDriver keeps sending the driver commands for pullFile, pullFolder and
pushFile.
AppiumDriverextendsio.appium.java_client.remote.AppiumRemoteWebDriver, and the elements it returns areio.appium.java_client.remote.AppiumWebElement. Both implementWebDriverandWebElementfromselenium-api, so code that is typed against the interfaces does not need any change.AppiumCommandExecutordoes not extendHttpCommandExecutoranymore. Its constructors acceptAppiumCommandInfomaps, andAppiumDriverLocalServiceinstead ofDriverService.AppiumW3CHttpCommandCodecdoes not extend the Selenium W3C codec anymore, andErrorCodesMobileextendsio.appium.java_client.remote.ErrorCodes.
- Custom HTTP clients and filters must implement the new
HttpClient,FilterandHttpHandlerinterfaces, which have the same shape as the Selenium ones. The default implementation is based onjava.net.http, as before. The Selenium client implementations (Netty, OkHttp) do not plug in anymore. AppiumClientConfigdoes not extend the SeleniumClientConfiganymore. The instances are immutable, and the methods that change a setting return a new instance, as before.AppiumClientConfig#withFilterkeeps the Appium user agent and idempotency filters, and the WebSocket timeout is not reset to the default anymore when other settings are changed.AppiumClientConfig.directConnect, the default timeouts and the default HTTP version are not changed.
AppiumDriverLocalService#closestops the server.- A server that fails to start now always throws
AppiumServerHasNotBeenStartedLocallyException.
- The page factory and
FluentWaitengine is nowio.appium.java_client.support. Page objects that use the Selenium@FindBy,@FindBys,@FindAlland@CacheLookupannotations keep working ifselenium-supportis available, but it is recommended to switch to the annotations fromio.appium.java_client.support. See Page objects.
The deprecated touch actions API is removed. A tap with W3C Actions:
var finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
var tap = new Sequence(finger, 0)
.addAction(finger.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), x, y))
.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()))
.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
driver.perform(List.of(tap));The gestures can also be performed with the commands of the Appium drivers, for example
driver.executeScript("mobile: clickGesture", Map.of("x", x, "y", y)) for UiAutomator2 and
driver.executeScript("mobile: tap", Map.of("x", x, "y", y)) for XCUITest. See the driver documentation
for the full list of the gesture commands.
AppiumDriverdoes not implementHasBiDianymore and thegetBiDiandmaybeGetBiDimethods are removed. Selenium deprecated them for removal and changed the BiDi API in an incompatible way in the recent releases.- To use the Selenium BiDi modules (for example
LogInspector), add theio.appium:java-client-selenium-bridgeartifact and wrap the driver withSeleniumBridge.asRemoteWebDriver(driver). See Selenium interoperability.
- The runtime classpath of the client is
selenium-api, Gson, ByteBuddy, SLF4J API and JSpecify. Guava, OpenTelemetry,selenium-manager,selenium-remote-driver,selenium-http,selenium-jsonandselenium-osare not brought transitively anymore. Declare the ones your code uses. - Pinning Selenium: only
selenium-apihas to be pinned, see Transitive dependencies management.