升级到 Selenium 4

您还在使用 Selenium 3 吗?本指南将帮助您升级到最新版本!

如果您使用的是官方支持的语言之一(Ruby、JavaScript、C#、Python 和 Java),升级到 Selenium 4 应该是一个无痛的过程。 在某些情况下,可能会发生一些问题,本指南将帮助您解决这些问题。 我们将介绍升级项目依赖项的步骤,并了解版本升级带来的主要弃用和更改。

以下是我们升级到 Selenium 4 的步骤

  • 准备我们的测试代码
  • 升级依赖项
  • 潜在的错误和弃用消息

注意:在开发 Selenium 3.x 版本时,实现了对 W3C WebDriver 标准的支持。 支持新的协议和旧的 JSON Wire 协议。 大约在 3.11 版本左右,Selenium 代码开始符合 W3C 1 级规范。 最新版本的 Selenium 3 中符合 W3C 的代码将在 Selenium 4 中按预期工作。

准备我们的测试代码

Selenium 4 移除了对旧协议的支持,并在底层默认使用 W3C WebDriver 标准。 对于大多数情况,此实现不会影响最终用户。 主要例外是 CapabilitiesActions 类。

能力

如果测试能力没有按照 W3C 标准进行结构化,可能会导致会话无法启动。 以下是 W3C WebDriver 标准能力的列表

  • browserName
  • browserVersion(替换 version
  • platformName(替换 platform
  • acceptInsecureCerts
  • pageLoadStrategy
  • proxy
  • timeouts
  • unhandledPromptBehavior

标准功能的最新列表可以在 W3C WebDriver 中找到。

任何未包含在上述列表中的功能都需要包含供应商前缀。 这适用于浏览器特定功能以及云供应商特定功能。 例如,如果您的云供应商使用 buildname 功能进行测试,则需要将它们包装在 cloud:options 块中(请咨询您的云供应商以获取适当的前缀)。

之前

移动代码

DesiredCapabilities caps = DesiredCapabilities.firefox();
caps.setCapability("platform", "Windows 10");
caps.setCapability("version", "92");
caps.setCapability("build", myTestBuild);
caps.setCapability("name", myTestName);
WebDriver driver = new RemoteWebDriver(new URL(cloudUrl), caps);
caps = {};
caps['browserName'] = 'Firefox';
caps['platform'] = 'Windows 10';
caps['version'] = '92';
caps['build'] = myTestBuild;
caps['name'] = myTestName;
DesiredCapabilities caps = new DesiredCapabilities();
caps.SetCapability("browserName", "firefox");
caps.SetCapability("platform", "Windows 10");
caps.SetCapability("version", "92");
caps.SetCapability("build", myTestBuild);
caps.SetCapability("name", myTestName);
var driver = new RemoteWebDriver(new Uri(CloudURL), caps);
      caps = Selenium::WebDriver::Remote::Capabilities.firefox
      caps[:platform] = 'Windows 10'
      caps[:version] = '92'
      caps[:build] = my_test_build
      caps[:name] = my_test_name
      driver = Selenium::WebDriver.for :remote, url: cloud_url, desired_capabilities: caps
      driver.get(url)
      driver.quit
caps = {}
caps['browserName'] = 'firefox'
caps['platform'] = 'Windows 10'
caps['version'] = '92'
caps['build'] = my_test_build
caps['name'] = my_test_name
driver = webdriver.Remote(cloud_url, desired_capabilities=caps)

之后

移动代码

FirefoxOptions browserOptions = new FirefoxOptions();
browserOptions.setPlatformName("Windows 10");
browserOptions.setBrowserVersion("92");
Map<String, Object> cloudOptions = new HashMap<>();
cloudOptions.put("build", myTestBuild);
cloudOptions.put("name", myTestName);
browserOptions.setCapability("cloud:options", cloudOptions);
WebDriver driver = new RemoteWebDriver(new URL(cloudUrl), browserOptions);
capabilities = {
  browserName: 'firefox',
  browserVersion: '92',
  platformName: 'Windows 10',
  'cloud:options': {
     build: myTestBuild,
     name: myTestName,
  }
}
var browserOptions = new FirefoxOptions();
browserOptions.PlatformName = "Windows 10";
browserOptions.BrowserVersion = "92";
var cloudOptions = new Dictionary<string, object>();
cloudOptions.Add("build", myTestBuild);
cloudOptions.Add("name", myTestName);
browserOptions.AddAdditionalOption("cloud:options", cloudOptions);
var driver = new RemoteWebDriver(new Uri(CloudURL), browserOptions);
      options = Selenium::WebDriver::Options.firefox
      options.platform_name = 'Windows 10'
      options.browser_version = 'latest'
      cloud_options = {}
      cloud_options[:build] = my_test_build
      cloud_options[:name] = my_test_name
      options.add_option('cloud:options', cloud_options)
      driver = Selenium::WebDriver.for :remote, capabilities: options
      driver.get(url)
      driver.quit
from selenium.webdriver.firefox.options import Options as FirefoxOptions
options = FirefoxOptions()
options.browser_version = '92'
options.platform_name = 'Windows 10'
cloud_options = {}
cloud_options['build'] = my_test_build
cloud_options['name'] = my_test_name
options.set_capability('cloud:options', cloud_options)
driver = webdriver.Remote(cloud_url, options=options)

Java 中查找元素实用方法

Java 绑定(FindsBy 接口)中用于查找元素的实用方法已被删除,因为它们仅供内部使用。 以下代码示例更好地解释了这一点。

使用 findElement* 查找单个元素

之前

driver.findElementByClassName("className");
driver.findElementByCssSelector(".className");
driver.findElementById("elementId");
driver.findElementByLinkText("linkText");
driver.findElementByName("elementName");
driver.findElementByPartialLinkText("partialText");
driver.findElementByTagName("elementTagName");
driver.findElementByXPath("xPath");
之后

driver.findElement(By.className("className"));
driver.findElement(By.cssSelector(".className"));
driver.findElement(By.id("elementId"));
driver.findElement(By.linkText("linkText"));
driver.findElement(By.name("elementName"));
driver.findElement(By.partialLinkText("partialText"));
driver.findElement(By.tagName("elementTagName"));
driver.findElement(By.xpath("xPath"));

使用 findElements* 查找多个元素

之前

driver.findElementsByClassName("className");
driver.findElementsByCssSelector(".className");
driver.findElementsById("elementId");
driver.findElementsByLinkText("linkText");
driver.findElementsByName("elementName");
driver.findElementsByPartialLinkText("partialText");
driver.findElementsByTagName("elementTagName");
driver.findElementsByXPath("xPath");
之后

driver.findElements(By.className("className"));
driver.findElements(By.cssSelector(".className"));
driver.findElements(By.id("elementId"));
driver.findElements(By.linkText("linkText"));
driver.findElements(By.name("elementName"));
driver.findElements(By.partialLinkText("partialText"));
driver.findElements(By.tagName("elementTagName"));
driver.findElements(By.xpath("xPath"));

升级依赖项

查看下面的小节以安装 Selenium 4 并升级项目依赖项。

Java

升级 Selenium 的过程取决于正在使用的构建工具。 我们将介绍 Java 最常用的构建工具,它们是 MavenGradle。 所需的最低 Java 版本仍然是 8。

Maven

之前

<dependencies>
  <!-- more dependencies ... -->
  <dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.141.59</version>
  </dependency>
  <!-- more dependencies ... -->
</dependencies>
之后

<dependencies>
    <!-- more dependencies ... -->
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.4.0</version>
    </dependency>
    <!-- more dependencies ... -->
</dependencies>

进行更改后,您可以在 pom.xml 文件所在的同一目录中执行 mvn clean compile

Gradle

之前

plugins {
    id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
    mavenCentral()
}
dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
    implementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.141.59'
}
test {
    useJUnitPlatform()
}
之后

plugins {
    id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
    mavenCentral()
}
dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
    implementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '4.4.0'
}
test {
    useJUnitPlatform()
}

进行更改后,您可以在 build.gradle 文件所在的同一目录中执行 ./gradlew clean build

要检查所有 Java 版本,您可以前往 MVNRepository

C#

在 C# 中获取 Selenium 4 更新的地方是 NuGet。 在 Selenium.WebDriver 包下,您可以获取更新到最新版本的说明。 在 Visual Studio 中,通过 NuGet 包管理器,您可以执行

PM> Install-Package Selenium.WebDriver -Version 4.4.0

Python

使用 Python 最重要的更改是最低要求的版本。 Selenium 4 将需要最低 Python 3.7 或更高版本。 更多详细信息可以在 Python 包索引中找到。 要从命令行升级,您可以执行

pip install selenium==4.4.3

Ruby

可以在 RubyGems 的 selenium-webdriver gem 中查看 Selenium 4 的更新详细信息。 要安装最新版本,您可以执行

gem install selenium-webdriver

将其添加到您的 Gemfile 中

gem 'selenium-webdriver', '~> 4.4.0'

JavaScript

selenium-webdriver 包可以在 Node 包管理器 npmjs 中找到。 可以在这里找到 Selenium 4。 要安装它,您可以执行

npm install selenium-webdriver

或者,更新您的 package.json 并运行 npm install

{
  "name": "selenium-tests",
  "version": "1.0.0",
  "dependencies": {
    "selenium-webdriver": "^4.4.0"
  }
}

潜在的错误和弃用消息

这里有一组代码示例,将帮助您克服升级到 Selenium 4 后可能遇到的弃用消息。

Java

等待和超时

Timeout 中接收的参数已从期望 (long time, TimeUnit unit) 切换为期望 (Duration duration)

之前

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.manage().timeouts().setScriptTimeout(2, TimeUnit.MINUTES);
driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);
之后

driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
driver.manage().timeouts().scriptTimeout(Duration.ofMinutes(2));
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(10));

现在,等待也需要不同的参数。 WebDriverWait 现在期望使用 Duration 而不是用于超时(以秒和毫秒为单位)的 longFluentWait 中的 withTimeoutpollingEvery 实用方法已从期望 (long time, TimeUnit unit) 切换为期望 (Duration duration)

之前

new WebDriverWait(driver, 3)
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("#id")));

Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
  .withTimeout(30, TimeUnit.SECONDS)
  .pollingEvery(5, TimeUnit.SECONDS)
  .ignoring(NoSuchElementException.class);
之后

new WebDriverWait(driver, Duration.ofSeconds(3))
  .until(ExpectedConditions.elementToBeClickable(By.cssSelector("#id")));

  Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
  .withTimeout(Duration.ofSeconds(30))
  .pollingEvery(Duration.ofSeconds(5))
  .ignoring(NoSuchElementException.class);

合并功能不再更改调用对象

可以将一组不同的功能合并到另一组功能中,并且它正在改变调用对象。 现在,需要分配合并操作的结果。

之前

MutableCapabilities capabilities = new MutableCapabilities();
capabilities.setCapability("platformVersion", "Windows 10");
FirefoxOptions options = new FirefoxOptions();
options.setHeadless(true);
options.merge(capabilities);

// As a result, the `options` object was getting modified.
之后

MutableCapabilities capabilities = new MutableCapabilities();
capabilities.setCapability("platformVersion", "Windows 10");
FirefoxOptions options = new FirefoxOptions();
options.setHeadless(true);
options = options.merge(capabilities);

// The result of the `merge` call needs to be assigned to an object.

Firefox 旧版

在 GeckoDriver 出现之前,Selenium 项目有一个驱动程序实现来自动化 Firefox(版本 <48)。 但是,由于此实现无法在最新版本的 Firefox 中工作,因此不再需要此实现。 为了避免升级到 Selenium 4 时出现重大问题,setLegacy 选项将显示为已弃用。 建议停止使用旧实现,而仅依赖 GeckoDriver。 以下代码将在升级后显示 setLegacy 行已弃用。

FirefoxOptions options = new FirefoxOptions();
options.setLegacy(true);

BrowserType

BrowserType 接口已经存在很长时间了,但它已被弃用,转而使用新的 Browser 接口。

之前

MutableCapabilities capabilities = new MutableCapabilities();
capabilities.setCapability("browserVersion", "92");
capabilities.setCapability("browserName", BrowserType.FIREFOX);
之后

MutableCapabilities capabilities = new MutableCapabilities();
capabilities.setCapability("browserVersion", "92");
capabilities.setCapability("browserName", Browser.FIREFOX);

C#

AddAdditionalCapability 已弃用

建议使用 AddAdditionalOption 代替。 这是一个显示此内容的示例

之前

var browserOptions = new ChromeOptions();
browserOptions.PlatformName = "Windows 10";
browserOptions.BrowserVersion = "latest";
var cloudOptions = new Dictionary<string, object>();
browserOptions.AddAdditionalCapability("cloud:options", cloudOptions, true);
之后

var browserOptions = new ChromeOptions();
browserOptions.PlatformName = "Windows 10";
browserOptions.BrowserVersion = "latest";
var cloudOptions = new Dictionary<string, object>();
browserOptions.AddAdditionalOption("cloud:options", cloudOptions);

Python

executable_path 已被弃用,请传入一个 Service 对象

在 Selenium 4 中,您需要从 Service 对象设置驱动程序的 executable_path,以防止出现弃用警告。(或者不设置路径,而是确保您需要的驱动程序在系统 PATH 中。)

之前

from selenium import webdriver
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(
    executable_path=CHROMEDRIVER_PATH, 
    options=options
)
之后

from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
options = webdriver.ChromeOptions()
service = ChromeService(executable_path=CHROMEDRIVER_PATH)
driver = webdriver.Chrome(service=service, options=options)

总结

我们介绍了升级到 Selenium 4 时要考虑的主要更改。 涵盖了为升级准备测试代码时要涵盖的不同方面,包括关于如何防止在使用新版本的 Selenium 时可能出现的潜在问题的建议。 最后,我们还介绍了升级后您可能会遇到的可能问题,并分享了这些问题的潜在修复方法。

最初发布于 https://saucelabs.com/resources/articles/how-to-upgrade-to-selenium-4