The Selenium team has spent a good amount of time making the upgrade process as painless as possible. A few things have been deprecated, so you might hit a couple of issues while upgrading, especially if you have built custom functionalities in your testing framework. This guide will show you how to move from Selenium 3 to Selenium 4.
Upgrading to Selenium 4 should be a painless process if you are using one of the officially supported languages (Ruby, JavaScript, C#, Python, and Java). There might be some cases where a few issues can happen, and this guide will help you to sort them out. We will go through the steps to upgrade your project dependencies and understand the major deprecations and changes the new version upgrade brings.
Note: while Selenium 3.x versions were being developed, support for the W3C WebDriver standard was implemented. Both this new protocol and the legacy JSON Wire Protocol was supported. Around version 3.11, Selenium code became compliant with the Level 1 W3C Recommendation. W3C compliant code in the latest version of Selenium 3 will work as expected in Selenium 4.
Selenium 4 removes support for the legacy protocol and uses the W3C WebDriver standard by default under the hood. For most things, this implementation will not affect end users. The major exceptions are Capabilities and the Actions class.
If the test capabilities are not structured to be W3C compliant, may cause a session to not be started. Here is the list of W3C WebDriver standard capabilities:
browserName browserVersion (replaces version) platformName (replaces platform) acceptInsecureCerts pageLoadStrategy proxy timeouts unhandledPromptBehavior
An up-to-date list of standard capabilities can be found at W3C WebDriver.
Any capability that is not contained in the list above, needs to include a vendor prefix. This applies to browser specific capabilities as well as Sauce Labs specific capabilities. For example, if we use the build and name capabilities in our tests, we need to wrap them in a sauce:options block (a complete example is shown below).
The use of browser Options instead of static browser methods of DesiredCapabilities has been suggested since Selenium 3.8. Those static methods have been removed in Selenium 4. This means that DesiredCapabilities.chrome() or DesiredCapabilities.firefox() and similar are not present anymore. See the examples below to migrate from those static methods to browser Options.
For example:
1DesiredCapabilities caps = DesiredCapabilities.chrome();2DesiredCapabilities caps = DesiredCapabilities.edge();3DesiredCapabilities caps = DesiredCapabilities.firefox();4DesiredCapabilities caps = DesiredCapabilities.internetExplorer();5DesiredCapabilities caps = DesiredCapabilities.safari();67// Are replaced by:89ChromeOptions browserOptions = new ChromeOptions();10EdgeOptions browserOptions = new EdgeOptions();11FirefoxOptions browserOptions = new FirefoxOptions();12InternetExplorerOptions browserOptions = new InternetExplorerOptions();13SafariOptions browserOptions = new SafariOptions();14
Using browser Options
simplifies the configuration needed to start a new session, allows setting browser-specific settings (like headless in Chrome), and reduces the chances of browser misconfiguration.
Following, we can see a code block with the old usage of capability names and DesiredCapabilities. Followed by a block that shows how the code needs to be updated.
The example is shown in the different official languages supported by Selenium. When browser Options are preferred when available, platform and version are replaced by platformName and browserVersion, and Sauce Labs specific capabilities are placed inside a sauce:options block.
Java
1DesiredCapabilities caps = DesiredCapabilities.chrome();2DesiredCapabilities caps = DesiredCapabilities.edge();3DesiredCapabilities caps = DesiredCapabilities.firefox();4DesiredCapabilities caps = DesiredCapabilities.internetExplorer();5DesiredCapabilities caps = DesiredCapabilities.safari();67// Are replaced by:89ChromeOptions browserOptions = new ChromeOptions();10EdgeOptions browserOptions = new EdgeOptions();11FirefoxOptions browserOptions = new FirefoxOptions();12InternetExplorerOptions browserOptions = new InternetExplorerOptions();13SafariOptions browserOptions = new SafariOptions();14
JavaScript
1// Before:23caps = {};4caps['browserName'] = 'Firefox';5caps['platform'] = 'Windows 10';6caps['version'] = '92';7caps['build'] = myTestBuild;8caps['name'] = myTestName;910//After:1112capabilities = {13browserName: 'firefox',14browserVersion: '92',15platformName: 'Windows 10',16'sauce:options': {17build: myTestBuild,18name: myTestName,19}20}21
C#
1// Before:23DesiredCapabilities caps = new DesiredCapabilities();4caps.SetCapability("browserName", "firefox");5caps.SetCapability("platform", "Windows 10");6caps.SetCapability("version", "92");7caps.SetCapability("build", myTestBuild);8caps.SetCapability("name", myTestName);9var driver = new RemoteWebDriver(new Uri(SauceURL), capabilities);1011// After:1213var browserOptions = new FirefoxOptions();14browserOptions.PlatformName = "Windows 10";15browserOptions.BrowserVersion = "92";16var sauceOptions = new Dictionary<string, object>();17sauceOptions.Add("build", myTestBuild);18sauceOptions.Add("name", myTestName);19browserOptions.AddAdditionalOption("sauce:options", sauceOptions);20var driver = new RemoteWebDriver(new Uri(SauceURL), options);21
Ruby
1// Before:23caps = Selenium::WebDriver::Remote::Capabilities.firefox4caps[:platform] = 'Windows 10'5caps[:version] = '92'6caps[:build] = my_test_build7caps[:name] = my_test_name8driver = Selenium::WebDriver.for :remote, url: sauce_url, desired_capabilities: caps910// After:1112options = Selenium::WebDriver::Options.firefox13options.browser_version = 'latest'14options.platform_name = 'Windows 10'15sauce_options = {}16sauce_options[:build] = my_test_build17sauce_options[:name] = my_test_name18options.add_option('sauce:options', sauce_options)19driver = Selenium::WebDriver.for :remote, url: sauce_url, capabilities: options20
Python
1// Before:23caps = {}4caps['browserName'] = 'firefox'5caps['platform'] = 'Windows 10'6caps['version'] = '92'7caps['build'] = my_test_build8caps['name'] = my_test_name9driver = webdriver.Remote(sauce_url, desired_capabilities=caps)1011// After:1213from selenium.webdriver.firefox.options import Options as FirefoxOptions14options = FirefoxOptions()15options.browser_version = '92'16options.platform_name = 'Windows 10'17sauce_options = {}18sauce_options['build'] = my_test_build19sauce_options['name'] = my_test_name20options.set_capability('sauce:options', sauce_options)21driver = webdriver.Remote(sauce_url, options=options)22
For more combinations and examples, check the Sauce Labs platform configurator.
The utility methods to find elements in the Java bindings (FindsBy
interfaces) have been removed as they were meant for internal use only. The following code samples explain this better.
1// Before:23driver.findElementByClassName("className");4driver.findElementByCssSelector(".className");5driver.findElementById("elementId");6driver.findElementByLinkText("linkText");7driver.findElementByName("elementName");8driver.findElementByPartialLinkText("partialText");9driver.findElementByTagName("elementTagName");10driver.findElementByXPath("xPath");1112// After:1314driver.findElement(By.className("className"));15driver.findElement(By.cssSelector(".className"));16driver.findElement(By.id("elementId"));17driver.findElement(By.linkText("linkText"));18driver.findElement(By.name("elementName"));19driver.findElement(By.partialLinkText("partialText"));20driver.findElement(By.tagName("elementTagName"));21driver.findElement(By.xpath("xPath"));2223// All the findElements* have been removed as well.2425//Before:2627driver.findElementsByClassName("className");28driver.findElementsByCssSelector(".className");29driver.findElementsById("elementId");30driver.findElementsByLinkText("linkText");31driver.findElementsByName("elementName");32driver.findElementsByPartialLinkText("partialText");33driver.findElementsByTagName("elementTagName");34driver.findElementsByXPath("xPath");3536// After:3738driver.findElements(By.className("className"));39driver.findElements(By.cssSelector(".className"));40driver.findElements(By.id("elementId"));41driver.findElements(By.linkText("linkText"));42driver.findElements(By.name("elementName"));43driver.findElements(By.partialLinkText("partialText"));44driver.findElements(By.tagName("elementTagName"));45driver.findElements(By.xpath("xPath"));46
Check the subsections below to install Selenium 4 and have your project dependencies upgraded.
Java
The process of upgrading Selenium depends on which build tool is being used. We will cover the most common ones for Java, which are Maven and Gradle. The minimum Java version required is still 8.
Maven
1// Before:23<dependencies>4<!-- more dependencies ... -->5<dependency>6<groupId>org.seleniumhq.selenium</groupId>7<artifactId>selenium-java</artifactId>8<version>3.141.59</version>9</dependency>10<!-- more dependencies ... -->11</dependencies>1213// After:1415<dependencies>16<!-- more dependencies ... -->17<dependency>18<groupId>org.seleniumhq.selenium</groupId>19<artifactId>selenium-java</artifactId>20<version>4.0.0</version>21</dependency>22<!-- more dependencies ... -->23</dependencies>24
After making the change, you could execute mvn clean compile
on the same directory where the pom.xml
file is.
Gradle
1// Before:23plugins {4id 'java'5}67group 'org.example'8version '1.0-SNAPSHOT'910repositories {11mavenCentral()12}1314dependencies {15testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'16testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'17implementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.141.59'18}1920test {21useJUnitPlatform()2223}2425// After:2627plugins {28id 'java'29}3031group 'org.example'32version '1.0-SNAPSHOT'3334repositories {35mavenCentral()36}3738dependencies {39testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'40testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'41implementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '4.0.0'42}4344test {45useJUnitPlatform()46}47
After making the change, you could execute ./gradlew clean build on the same directory where the build.gradle file is.
To check all the Java releases, you can head to MVNRepository.
C#
The place to get updates for Selenium 4 in C# is NuGet. Under the Selenium.WebDriver package you can get the instructions to update to the latest version. Inside of Visual Studio, through the NuGet Package Manager you can execute:
PM> Install-Package Selenium.WebDriver -Version 4.0.0
Python
The most important change to use Python is the minimum required version. Selenium 4 will require a minimum Python 3.7 or higher. More details can be found at the Python Package Index. To upgrade from the command line, you can execute:
pip install selenium==4.0.0
Ruby
The update details for Selenium 4 can be seen at the selenium-webdriver gem in RubyGems. To install the latest version, you can execute:
gem install selenium-webdriver
To add it to your Gemfile:
gem 'selenium-webdriver', '~> 4.0.0'
JavaScript
The selenium-webdriver package can be found at the Node package manager, npmjs. Selenium 4 can be found here. To install it, you could either execute: npm install selenium-webdriver
Or, update your package. json and run npm install:
{ "name": "selenium-tests", "version": "1.0.0", "dependencies": { "selenium-webdriver": "^4.0.0" } }
Here is a set of code examples that will help to overcome the deprecation messages you might encounter after upgrading to Selenium 4.
Waits and Timeout
The parameters received in Timeout have switched from expecting (long time, TimeUnit unit) to expect (Duration duration).
1// Before:23driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);4driver.manage().timeouts().setScriptTimeout(2, TimeUnit.MINUTES);5driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);67// After:89driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));10driver.manage().timeouts().scriptTimeout(Duration.ofMinutes(2));11driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(10));12
Waits are also expecting different parameters now. WebDriverWait is now expecting a Duration instead of a long for timeout in seconds and milliseconds. The withTimeout and pollingEvery utility methods from FluentWait have switched from expecting (long time, TimeUnit unit) to expect (Duration duration).
1// Before:23new WebDriverWait(driver, 3)4.until(ExpectedConditions.elementToBeClickable(By.cssSelector("#id")));56Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)7.withTimeout(30, TimeUnit.SECONDS)8.pollingEvery(5, TimeUnit.SECONDS)9.ignoring(NoSuchElementException.class);1011// After:1213new WebDriverWait(driver, Duration.ofSeconds(3))14.until(ExpectedConditions.elementToBeClickable(By.cssSelector("#id")));1516Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)17.withTimeout(Duration.ofSeconds(30))18.pollingEvery(Duration.ofSeconds(5))19.ignoring(NoSuchElementException.class);20
It was possible to merge a different set of capabilities into another set, and it was mutating the calling object. Now, the result of the merge operation needs to be assigned.
1// Before:23MutableCapabilities capabilities = new MutableCapabilities();4capabilities.setCapability("platformVersion", "Windows 10");5FirefoxOptions options = new FirefoxOptions();6options.setHeadless(true);7options.merge(capabilities);89// As a result, the options object was getting modified.10// After:1112MutableCapabilities capabilities = new MutableCapabilities();13capabilities.setCapability("platformVersion", "Windows 10");14FirefoxOptions options = new FirefoxOptions();15options.setHeadless(true);16options = options.merge(capabilities);17
The result of the merge
call needs to be assigned to an object.
Before GeckoDriver was around, the Selenium project had a driver implementation to automate Firefox (version <48). However, this implementation is not needed anymore as it does not work in recent versions of Firefox. To avoid major issues when upgrading to Selenium 4, the setLegacy
option will be shown as deprecated. The recommendation is to stop using the old implementation and rely only on GeckoDriver. The following code will show the setLegacy
line deprecated after upgrading.
FirefoxOptions options = new FirefoxOptions(); options.setLegacy(true);
The BrowserType interface has been around for a long time, however it is getting deprecated in favor of the new Browser interface.
1// Before:23MutableCapabilities capabilities = new MutableCapabilities();4capabilities.setCapability("browserVersion", "92");5capabilities.setCapability("browserName", BrowserType.FIREFOX);67// After:89MutableCapabilities capabilities = new MutableCapabilities();10capabilities.setCapability("browserVersion", "92");11capabilities.setCapability("browserName", Browser.FIREFOX);12
AddAdditionalCapability is deprecated. Instead, AddAdditionalOption is recommended. Here is an example showing this:
1// Before:23var browserOptions = new ChromeOptions();4browserOptions.PlatformName = "Windows 10";5browserOptions.BrowserVersion = "latest";6var sauceOptions = new Dictionary<string, object>();7browserOptions.AddAdditionalCapability("sauce:options", sauceOptions, true);89// After:1011var browserOptions = new ChromeOptions();12browserOptions.PlatformName = "Windows 10";13browserOptions.BrowserVersion = "latest";14var sauceOptions = new Dictionary<string, object>();15browserOptions.AddAdditionalOption("sauce:options", sauceOptions);16
We went through the major changes to be taken into consideration when upgrading to Selenium 4. Covering the different aspects to cover when test code is prepared for the upgrade, including suggestions on how to prevent potential issues that can show up when using the new version of Selenium. To finalize, we also covered a set of possible issues that you can bump into after upgrading, and we shared potential fixes for those issues.