这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

故障排除协助

如何管理 WebDriver 的问题.

Selenium错误的根本原因并不总是很明显.

  1. 最常见的Selenium相关错误, 是源自未及时同步的结果. 请阅读 等待策略. 当遇到一个问题, 如果不确定是否因为同步策略, 您可以尝试暂时硬编码一个较大的休眠时间, 您将明确添加显式等待是否有帮助.

  2. 请注意, 报告给项目的许多错误, 实际上是由Selenium向其发送命令的基础驱动程序所引起的. 您可以通过执行 浏览器 中的 多个命令来解决驱动程序问题.

  3. 如果您对如何执行有疑惑, 请查看 支持选项 获取帮助的方法.

  4. 如果您认为您发现了Selenium代码的问题, 请在Github上提交 问题报告.

1 - 理解常见的异常

如何处理Selenium代码中的各种问题.

The W3C WebDriver specification defines the errors a driver can return. Each language binding turns them into its own exception (or error) class, and the names are not always the same. Kotlin uses the Java classes, the Ruby classes are in Selenium::WebDriver::Error, and the JavaScript classes are in the selenium-webdriver/lib/error module.

W3C errorJavaPythonCSharpRubyJavaScript
detached shadow rootDetachedShadowRootExceptionDetachedShadowRootExceptionDetachedShadowRootExceptionDetachedShadowRootErrorDetachedShadowRootError
element click interceptedElementClickInterceptedExceptionElementClickInterceptedExceptionElementClickInterceptedExceptionElementClickInterceptedErrorElementClickInterceptedError
element not interactableElementNotInteractableExceptionElementNotInteractableExceptionElementNotInteractableExceptionElementNotInteractableErrorElementNotInteractableError
insecure certificateInsecureCertificateExceptionInsecureCertificateExceptionInsecureCertificateExceptionInsecureCertificateErrorInsecureCertificateError
invalid argumentInvalidArgumentExceptionInvalidArgumentExceptionWebDriverArgumentExceptionInvalidArgumentErrorInvalidArgumentError
invalid cookie domainInvalidCookieDomainExceptionInvalidCookieDomainExceptionInvalidCookieDomainExceptionInvalidCookieDomainErrorInvalidCookieDomainError
invalid element stateInvalidElementStateExceptionInvalidElementStateExceptionInvalidElementStateExceptionInvalidElementStateErrorInvalidElementStateError
invalid selectorInvalidSelectorExceptionInvalidSelectorExceptionInvalidSelectorExceptionInvalidSelectorErrorInvalidSelectorError
invalid session idNoSuchSessionExceptionInvalidSessionIdExceptionWebDriverExceptionInvalidSessionIdErrorNoSuchSessionError
javascript errorJavascriptExceptionJavascriptExceptionJavaScriptExceptionJavascriptErrorJavascriptError
move target out of boundsMoveTargetOutOfBoundsExceptionMoveTargetOutOfBoundsExceptionMoveTargetOutOfBoundsExceptionMoveTargetOutOfBoundsErrorMoveTargetOutOfBoundsError
no such alertNoAlertPresentExceptionNoAlertPresentExceptionNoAlertPresentExceptionNoSuchAlertErrorNoSuchAlertError
no such cookieNoSuchCookieExceptionNoSuchCookieExceptionNoSuchCookieExceptionNoSuchCookieErrorNoSuchCookieError
no such elementNoSuchElementExceptionNoSuchElementExceptionNoSuchElementExceptionNoSuchElementErrorNoSuchElementError
no such frameNoSuchFrameExceptionNoSuchFrameExceptionNoSuchFrameExceptionNoSuchFrameErrorNoSuchFrameError
no such shadow rootNoSuchShadowRootExceptionNoSuchShadowRootExceptionNoSuchShadowRootExceptionNoSuchShadowRootErrorNoSuchShadowRootError
no such windowNoSuchWindowExceptionNoSuchWindowExceptionNoSuchWindowExceptionNoSuchWindowErrorNoSuchWindowError
script timeoutScriptTimeoutExceptionTimeoutExceptionWebDriverTimeoutExceptionScriptTimeoutErrorScriptTimeoutError
session not createdSessionNotCreatedExceptionSessionNotCreatedExceptionInvalidOperationExceptionSessionNotCreatedErrorSessionNotCreatedError
stale element referenceStaleElementReferenceExceptionStaleElementReferenceExceptionStaleElementReferenceExceptionStaleElementReferenceErrorStaleElementReferenceError
timeoutTimeoutExceptionTimeoutExceptionWebDriverTimeoutExceptionTimeoutErrorTimeoutError
unable to capture screenScreenshotExceptionScreenshotExceptionInvalidOperationExceptionUnableToCaptureScreenErrorUnableToCaptureScreenError
unable to set cookieUnableToSetCookieExceptionUnableToSetCookieExceptionUnableToSetCookieExceptionUnableToSetCookieErrorUnableToSetCookieError
unexpected alert openUnhandledAlertExceptionUnexpectedAlertPresentExceptionUnhandledAlertExceptionUnexpectedAlertOpenErrorUnexpectedAlertOpenError
unknown commandUnsupportedCommandExceptionWebDriverExceptionNotImplementedExceptionUnknownCommandErrorUnknownCommandError
unknown errorWebDriverExceptionWebDriverExceptionUnknownErrorExceptionUnknownErrorWebDriverError
unknown methodUnsupportedCommandExceptionWebDriverExceptionUnknownMethodExceptionUnknownMethodErrorUnknownMethodError
unsupported operationUnsupportedCommandExceptionWebDriverExceptionUnsupportedOperationExceptionUnsupportedOperationErrorUnsupportedOperationError

All of these classes inherit from the base exception of their binding (WebDriverException in Java, Python and CSharp, WebDriverError in Ruby and JavaScript), except for the CSharp InvalidOperationException and NotImplementedException, which are .NET system exceptions.

无效选择器的异常 (InvalidSelectorException)

某些时候难以获得正确的CSS以及XPath选择器。

潜在原因

  • The CSS or XPath selector you are trying to use has invalid characters or an invalid query.
  • You may have placed an XPATH value as a parameter to a CSS selector, or vice versa.
  • You may have used a CSS or XPATH selector as a parameter to an ID selector.

可行方案

通过验证器服务运行选择器:

或者使用浏览器扩展程序来获取已知的良好值:

没有这样元素的异常 (NoSuchElementException)

在您尝试找到该元素的当前时刻无法定位元素。

潜在原因

  • 您在错误的位置寻找元素 (也许以前的操作不成功)
  • 您在错误的时间寻找元素 (该元素尚未显示在 DOM 中)
  • 自您编写代码以来定位器已变更

可行方案

  • 确保您位于期望的页面上,并且代码中的前置操作已正确完成
  • 确保您使用的是正确的 等待策略
  • Use an interactive Selenium Wait Code Generator to create accurate explicit wait snippets for multiple supported languages including Java, Python, C#, JavaScript, and Ruby.
  • 使用浏览器的devtools控制台更新定位器或使用浏览器扩展程序,例如:

过时元素引用的异常 (StaleElementReferenceException)

当成功定位到元素时, WebDriver会为其设置一个引用ID作为标记, 如果由于上下文环境发生变化, 导致之前元素的位置发生了变化或者无法找到了, WebDriver并不会自动重新定位, 任何使用之前元素所做的操作将报错该异常。

常见因素

以下情况可能发生此异常:

  • 您已刷新页面,或者页面的 DOM 已动态更改。
  • 您已导航到其他页面。
  • 您已切换到另一个窗口,或者进入/移出某个 frame / iframe。

常见方案

DOM已变更

当页面刷新或页面上的项目各处移动时, 页面上仍然有一个具有所需定位器的元素, 它只是不再被正在使用的元素对象访问, 并且必须重新定位该元素才能再次使用。

这往往通过以下两种方式之一完成:

  • 每次使用时都要重新定位元素。 尽管有可能元素在定位和使用元素之间的微秒内, 发生变化的可能性很小。 缺点是这不是最有效的方法, 尤其是在 Remote Grid上运行时。

  • 用另一个存储定位器的对象包装 Web 元素,并缓存定位的 Selenium 元素。 对该包装对象执行操作时,您可以尝试使用之前找到的缓存对象, 如果它是发生了变化,则可以捕获异常, 使用存储的定位器重新定位元素,并重试该方法。 这样效率更高,但如果您使用的定位器在页面更改后引用了不同的元素(而不是您想要的元素),则可能会导致问题。

上下文已变更

元素对象是针对特定的上下文存储的, 因此如果您切换到不同的上下文, 比如不同的 Window 或不同的 frame 或 iframe 元素引用仍然有效, 但暂时无法访问。在这种情况下, 重新定位元素无济于事,因为它在当前上下文中不存在。

要解决此问题,您需要确保在使用该元素之前切换回正确的上下文。

页面已变更

这种情况发生在您不仅更改了上下文, 而且导航到另一个页面并破坏了元素所在的上下文。 您无法仅从当前上下文重新定位它, 也无法切换回元素有效的活动上下文。 如果这是您的错误原因, 您必须回到正确的位置并重新定位元素。

ElementClickInterceptedException

This exception occurs when Selenium tries to click an element, but the click would instead be received by a different element. Before Selenium will click an element, it checks if the element is visible, unobscured by any other elements, and enabled - if the element is obscured, it will raise this exception.

Likely Cause

UI Elements Overlapping

Elements on the UI are typically placed next to each other, but occasionally elements may overlap. For example, a navbar always staying at the top of your window as you scroll a page. If that navbar happens to be covering an element we are trying to click, Selenium might believe it to be visible and enabled, but when you try to click it will throw this exception. Pop-ups and Modals are also common offenders here.

Animations

Elements with animations have the potential to cause this exception as well - it is recommended to wait for animations to cease before attempting to click an element.

Possible Solutions

Use Explicit Waits

Explicit Waits will likely be your best friend in these instances. A great way is to use ExpectedCondition.ToBeClickable() with WebDriverWait to wait until the right moment.

Scroll the Element into View

In instances where the element is out of view, but Selenium still registers the element as visible (e.g. navbars overlapping a section at the top of your screen), you can use the WebDriver.executeScript() method to execute a javascript function to scroll (e.g. WebDriver.executeScript('window.scrollBy(0,-250)')) or you can utilize the Actions class with Actions.moveToElement(element).

无效SessionId异常

有时您尝试访问的会话与当前可用的会话不同。

可能原因

通常发生在会话被删除时(例如:driver.quit())或会话发生更改时,例如最后一个标签页/浏览器已关闭(例如:driver.close())。

可能的解决方案

检查脚本中是否有 driver.close() 和 driver.quit() 的实例,以及其他可能导致标签页/浏览器关闭的原因。可能是您在应该/能够定位元素之前就尝试定位了该元素。

SessionNotCreatedException

此异常发生在 WebDriver 无法为浏览器创建新会话时。通常由于版本不匹配、系统级限制或配置问题导致。

可能的原因

  • 浏览器版本和 WebDriver 版本不兼容(例如 ChromeDriver v113 和 Chrome v115)。
  • macOS 隐私设置可能会阻止 WebDriver 运行。
  • WebDriver 二进制文件丢失、不可访问或没有执行权限。

可能的解决方案

  • 确保 WebDriver 版本与浏览器版本匹配。对于 Chrome,请在浏览器中访问 chrome://settings/help 检查浏览器版本,并从 ChromeDriver 下载页面下载匹配的驱动程序。
  • 在 macOS 上,转到 系统设置 > 隐私与安全性,并允许驱动程序运行(如果被阻止)。
  • 验证驱动程序二进制文件是否可执行(在 Linux/macOS 上运行 chmod +x /path/to/driver)。

ElementNotInteractableException

当 Selenium 尝试与当前状态下无法交互的元素进行交互时,会发生此异常。

可能的原因

  1. 不支持的操作:尝试对不支持操作的元素执行操作,例如对 <form> 或 <label> 使用 sendKeys。
  2. 多个元素匹配定位器:定位器匹配到非可交互的元素,例如 <td> 标签,而不是目标的 <input> 字段。
  3. 隐藏的元素:元素存在于 DOM 中,但由于 CSS、hidden 属性或元素超出可见视口范围而不可见。

可能的解决方案

  1. 根据元素类型使用适当的操作(例如,仅对 <input> 字段使用 sendKeys)。
  2. 确保定位器唯一标识目标元素,以避免错误匹配。
  3. 在与元素交互之前,检查其是否在页面上可见。如果需要,将元素滚动到视图中。
  4. 使用显式等待以确保元素在执行操作前可交互。

DetachedShadowRootException

A shadow root was found earlier, but it is no longer attached to the DOM. This is the shadow root equivalent of a StaleElementReferenceException.

Likely Cause

  • The page was refreshed or the user navigated to another page after the shadow root was found.
  • The shadow host element was removed from the DOM or replaced by a new element.

Possible Solutions

  • Find the shadow host element again and get its shadow root before using it.
  • See Evaluating the Shadow DOM for how to work with shadow roots.

InsecureCertificateException

Navigating to a page made the browser show a certificate warning, which is usually the result of an expired, self-signed or otherwise invalid TLS certificate.

Likely Cause

  • The site under test uses a self-signed certificate, which is common in test environments.
  • The certificate has expired or does not match the domain name.

Possible Solutions

  • If you trust the site, set the acceptInsecureCerts capability to true in the browser options.
  • Otherwise, fix the certificate of the site under test.

InvalidArgumentException

The arguments passed to a command are invalid or malformed.

Likely Cause

  • A value of the wrong type was passed to a command, e.g., a number where a string is expected.
  • A URL passed to get is not a valid absolute URL (e.g., it is missing https://).
  • A browser option or capability has a value the driver does not accept.
  • The file passed for a file upload does not exist. In a remote session, the file must exist on the machine where the browser runs, unless a Local File Detector is used, which sends the file from the machine running the test.

Possible Solutions

  • Read the error message, which usually says which argument is invalid.
  • Check the values you pass to the failing command, and the capabilities used to start the session.

InvalidCookieDomainException

A cookie was added for a domain that is different from the domain of the current page.

Likely Cause

  • You are adding a cookie before navigating to the site it belongs to (e.g., on about:blank or the browser start page).
  • The cookie’s domain value does not match the current page.
  • The current page does not accept cookies, e.g., a file:// or data: URL.

Possible Solutions

InvalidElementStateException

A command cannot be completed because the element is in a state that does not allow it.

Likely Cause

  • You are trying to clear an element that is not editable, such as a disabled or read-only input.
  • You are trying to change an element that is disabled.

Possible Solutions

  • Check that the element is enabled and editable before interacting with it.
  • Wait for the application to enable the element, using an explicit wait.

JavascriptException

The JavaScript code passed to the driver failed while running in the browser.

Likely Cause

  • The script has a syntax error or throws an error.
  • The script uses a variable or element that does not exist on the current page.
  • The script uses arguments that were not passed to it.

Possible Solutions

  • Read the error message, which includes the JavaScript error raised by the browser.
  • Run the script in the browser’s developer tools console to debug it.
  • Prefer Selenium commands over JavaScript when there is a command that does the same thing.

MoveTargetOutOfBoundsException

An Actions command tried to move the pointer to a point outside of the browser’s viewport.

Likely Cause

  • The offset used in a move command puts the pointer outside of the viewport.
  • The target element is outside of the viewport and cannot be scrolled into it.
  • The browser window is too small for the page.

Possible Solutions

  • Check the offsets used with the mouse and pen actions. Depending on the origin of the move, offsets are relative to the viewport, to the current pointer position, or to the center of an element.
  • Scroll the element into view first, e.g., with the wheel actions.
  • Use a bigger browser window.

NoAlertPresentException

A command tried to use an alert, confirm or prompt dialog, but no dialog was open.

Likely Cause

  • The dialog has not opened yet.
  • The dialog was already closed, either by your code or by the unhandledPromptBehavior of the session.
  • The page opens a dialog that is built with HTML (a modal), which is not a JavaScript alert.

Possible Solutions

NoSuchCookieException

No cookie with the given name exists for the current page.

Likely Cause

  • The cookie has not been set yet, it has expired, or it was deleted.
  • The cookie belongs to a different domain or path than the current page.

Possible Solutions

  • Check that you are on a page of the domain and path the cookie belongs to.
  • Get all cookies to check which ones are available.
  • See Working with cookies.

NoSuchFrameException

A command to switch to a frame or iframe could not find it.

Likely Cause

  • The frame has not been loaded yet.
  • The locator, index, name or ID used to switch to the frame is wrong.
  • The frame is inside another frame, and the driver is not switched to the parent frame.

Possible Solutions

NoSuchShadowRootException

An element does not have a shadow root.

Likely Cause

  • The element is not a shadow host.
  • The shadow root has not been attached yet.

Possible Solutions

  • Check in the browser’s developer tools that you are getting the shadow root of the shadow host element.
  • Wait for the shadow root to be attached before getting it.
  • See Evaluating the Shadow DOM.

NoSuchWindowException

The window or tab the command was sent to does not exist.

Likely Cause

  • The window or tab the driver is switched to was closed, by your code (e.g., driver.close()) or by the page.
  • The window handle used to switch to a window is wrong.

Possible Solutions

  • After closing a window or tab, switch to another open window before sending more commands.
  • Get the current window handles to check which windows are available.
  • See Working with windows and tabs.

ScreenshotException

The browser could not take a screenshot.

Likely Cause

  • The page or element is too big for the browser to capture, e.g., a full page screenshot of a very long page.

Possible Solutions

  • Take a screenshot of the viewport or of a smaller element instead.

ScriptTimeoutException

A script passed to the driver did not finish before the script timeout expired.

Likely Cause

  • An asynchronous script did not call the callback function it receives as its last argument.
  • The script takes longer than the script timeout, which is 30 seconds by default.

Possible Solutions

  • Check that asynchronous scripts call the callback function in every code path.
  • Increase the script timeout if the script needs more time.

TimeoutException

An operation did not finish before its timeout expired.

Likely Cause

  • An explicit wait timed out because its condition was not met in time. This is the most common cause, and the exception is raised by the binding, not the driver.
  • A page took longer to load than the page load timeout, which is 300 seconds by default.

Possible Solutions

  • Check that the condition you are waiting for can be met, e.g., that the locator is correct.
  • Increase the timeout of the wait, if the application needs more time.
  • Use a different page load strategy if you do not need to wait for the whole page to load.

UnableToSetCookieException

The browser could not set a cookie.

Likely Cause

  • The cookie has invalid values, e.g., a malformed name, or an expiry date in the past.
  • The cookie is set as secure, and the current page does not use HTTPS.

Possible Solutions

  • Check the values of the cookie.
  • Use HTTPS pages when setting secure cookies.
  • See Working with cookies.

UnhandledAlertException

An alert, confirm or prompt dialog is open, and it blocks the command.

Likely Cause

  • The page opened a dialog that your code did not expect or did not close.

Possible Solutions

UnsupportedCommandException

The driver does not support the command. Java uses this exception for the unknown command, unknown method and unsupported operation W3C errors. See the table at the top of this page for the class each binding uses for each of these errors.

Likely Cause

  • The command is not part of the W3C WebDriver specification, or the driver has not implemented it yet.
  • The command only works with some browsers (e.g., a browser specific command used with another browser).
  • The driver is older than the Selenium version being used.

Possible Solutions

  • Check that the browser and driver support the command.
  • Update the browser and driver. Selenium Manager gets the driver that matches your browser automatically.

WebDriverException

An unknown error happened in the driver while processing the command. It is the base exception of most bindings, so it is also raised for errors that do not have a more specific class.

Likely Cause

  • The browser crashed or was closed while the command was running.
  • The driver could not connect to the browser.
  • An error in the browser or driver that is not described by any other error.

Possible Solutions

  • Read the full error message, which usually has more details.
  • Update the browser and driver to the latest versions.
  • Enable logging to get more information.

ElementNotVisibleException

This exception is thrown when the element you are trying to interact with is present in the DOM, but is not visible.

Likely Cause

This can occur in several situations:

  • Another element is blocking your intended element
  • The element is disabled/invisible to the user

Possible Solutions

This issue cannot always be resolved on the user’s end, however when it can it is usually solved by the following: using an explicit wait, or interacting with the page in such a way to make the element visible (scrolling, clicking a button, etc.)

1.1 - Unable to Locate Driver Error

Troubleshooting missing path to driver executable.

Historically, this is the most common error beginning Selenium users get when trying to run code for the first time:

The path to the driver executable must be set by the webdriver.chrome.driver system property; for more information, see https://chromedriver.chromium.org/. The latest version can be downloaded from https://chromedriver.chromium.org/downloads
The executable chromedriver needs to be available in the path.
The file geckodriver does not exist. The driver can be downloaded at https://github.com/mozilla/geckodriver/releases"
Unable to locate the chromedriver executable;

Likely cause

Through WebDriver, Selenium supports all major browsers. In order to drive the requested browser, Selenium needs to send commands to it via an executable driver. This error means the necessary driver could not be found by any of the means Selenium attempts to use.

Possible solutions

There are several ways to ensure Selenium gets the driver it needs.

Use the latest version of Selenium

As of Selenium 4.6, Selenium downloads the correct driver for you. You shouldn’t need to do anything. If you are using the latest version of Selenium and you are getting an error, please turn on logging and file a bug report with that information.

If you want to read more information about how Selenium manages driver downloads for you, you can read about the Selenium Manager.

Use the PATH environment variable

This option first requires manually downloading the driver.

This is a flexible option to change location of drivers without having to update your code, and will work on multiple machines without requiring that each machine put the drivers in the same place.

You can either place the drivers in a directory that is already listed in PATH, or you can place them in a directory and add it to PATH.

To see what directories are already on PATH, open a Terminal and execute:

echo $PATH

If the location to your driver is not already in a directory listed, you can add a new directory to PATH:

echo 'export PATH=$PATH:/path/to/driver' >> ~/.bash_profile
source ~/.bash_profile

You can test if it has been added correctly by checking the version of the driver:

chromedriver --version

To see what directories are already on PATH, open a Terminal and execute:

echo $PATH

If the location to your driver is not already in a directory listed, you can add a new directory to PATH:

echo 'export PATH=$PATH:/path/to/driver' >> ~/.zshenv
source ~/.zshenv

You can test if it has been added correctly by checking the version of the driver:

chromedriver --version

To see what directories are already on PATH, open a Command Prompt and execute:

echo %PATH%

If the location to your driver is not already in a directory listed, you can add a new directory to PATH:

setx PATH "%PATH%;C:\WebDriver\bin"

You can test if it has been added correctly by checking the version of the driver:

chromedriver.exe --version

Specify the location of the driver

If you cannot upgrade to the latest version of Selenium, you do not want Selenium to download drivers for you, and you can’t figure out the environment variables, you can specify the location of the driver in the Service object.

You first need to download the desired driver, then create an instance of the applicable Service class and set the path.

Specifying the location in the code itself has the advantage of not needing to figure out Environment Variables on your system, but has the drawback of making the code less flexible.

Driver management libraries

Before Selenium managed drivers itself, other projects were created to do so for you.

If you can’t use Selenium Manager because you are using an older version of Selenium (please upgrade), or need an advanced feature not yet implemented by Selenium Manager, you might try one of these tools to keep your drivers automatically updated:

Download the driver

浏览器支持的操作系统维护者下载问题追溯
Chromium/ChromeWindows/macOS/LinuxGoogle下载Issues
FirefoxWindows/macOS/LinuxMozilla下载Issues
EdgeWindows/macOS/LinuxMicrosoft下载Issues
Internet ExplorerWindowsSelenium Project下载Issues
SafarimacOS High Sierra and newerApple内置Issues

备注:Opera驱动不再适用于Selenium的最新功能,目前官方不支持。

2 - 记录 Selenium 命令

获取Selenium的执行信息.

启用日志记录是获取额外信息的宝贵方法, 这些信息可能有助于您确定 遇到问题的原因.

获取一个logger

Java 日志通常是按类创建的. 您可以通过默认logger来使用所有loggers. 为了过滤特定类, 请参考 过滤器

获取根logger:

    public void logging() throws IOException {

Java日志并不简单直接, 如果您只是在寻找一种简单的方法 查看重要的Selenium日志, 请参阅 Selenium Logger 项目

Python logs are typically created per module. You can match all submodules by referencing the top level module. So to work with all loggers in selenium module, you can do this:

    logger = logging.getLogger('selenium')

You must also create and add a log handler (StreamHandler, FileHandler, etc).

To save logs to a file, you can do this:

log_path = '/path/to/log'
handler = logging.FileHandler(log_path)
logger.addHandler(handler)

To display logs in the console, you can do this:

handler = logging.StreamHandler()
logger.addHandler(handler)

.NET logger is managed with a static class, so all access to logging is managed simply by referencing Log from the OpenQA.Selenium.Internal.Logging namespace.

If you want to see as much debugging as possible in all the classes, you can turn on debugging globally in Ruby by setting $DEBUG = true.

For more fine-tuned control, Ruby Selenium created its own Logger class to wrap the default Logger class. This implementation provides some interesting additional features. Obtain the logger directly from the #loggerclass method on the Selenium::WebDriver module:

    logger = Selenium::WebDriver.logger
const logging = require('selenium-webdriver/lib/logging')
logger = logging.getLogger('webdriver')

日志级别

Logger级别有助于根据日志的严重性过滤日志.

Java有七种logger级别: SEVERE, WARNING, INFO, CONFIG, FINE, FINER, 以及 FINEST. 默认是 INFO.

您必须更改logger的级别和根logger上的处理程序的级别:

        Logger logger = Logger.getLogger("");
        logger.setLevel(Level.FINE);
        Arrays.stream(logger.getHandlers()).forEach(handler -> {
            handler.setLevel(Level.FINE);

Python has 6 logger levels: CRITICAL, ERROR, WARNING, INFO, DEBUG, and NOTSET. The default is WARNING

To change the level of the logger:

    logger.setLevel(logging.DEBUG)

Things get complicated when you use PyTest, though. By default, PyTest hides logging unless the test fails. You need to set 3 things to get PyTest to display logs on passing tests.

To always output logs with PyTest you need to run with additional arguments. First, -s to prevent PyTest from capturing the console. Second, -p no:logging, which allows you to override the default PyTest logging settings so logs can be displayed regardless of errors.

So you need to set these flags in your IDE, or run PyTest on command line like:

pytest -s -p no:logging

Finally, since you turned off logging in the arguments above, you now need to add configuration to turn it back on:

logging.basicConfig(level=logging.WARN)

.NET has 6 logger levels: Error, Warn, Info, Debug, Trace and None. The default level is Warn.

To change the level of the logger:

        {

Ruby logger has 5 logger levels: :debug, :info, :warn, :error, :fatal. As of Selenium v4.9.1, The default is :info.

To change the level of the logger:

    logger.level = :debug

JavaScript has 9 logger levels: OFF, SEVERE, WARNING, INFO, DEBUG, FINE, FINER, FINEST, ALL. The default is OFF.

To change the level of the logger:

logger.setLevel(logging.Level.INFO)

Actionable items

对于需要用户后续行动的操作, 会将其记录为警告. 这经常用于被弃用的内容. 由于各种原因, Selenium项目不遵循标准的语义版本控制实践. 我们的政策是将 3 个版本的内容标记为已弃用后, 再删除它们, 因此弃用可能记录为警告.

Java 日志可操作的内容在logger级别 WARN

Example:

May 08, 2023 9:23:38 PM dev.selenium.troubleshooting.LoggingTest logging
WARNING: this is a warning

Python logs actionable content at logger level — WARNING Details about deprecations are logged at this level.

Example:

WARNING  selenium:test_logging.py:23 this is a warning

.NET logs actionable content at logger level Warn.

Example:

11:04:40.986 WARN LoggingTest: this is a warning

Ruby logs actionable content at logger level — :warn. Details about deprecations are logged at this level.

For example:

2023-05-08 20:53:13 WARN Selenium [:example_id] this is a warning 

Because these items can get annoying, we’ve provided an easy way to turn them off, see filtering section below.

Useful information

这是默认级别, Selenium 记录用户应该注意但不需要对其执行操作的内容. 这可能会引用新方法或将用户定向到有关某些内容的详细信息

Java 日志有用的信息在logger 级别 INFO

Example:

May 08, 2023 9:23:38 PM dev.selenium.troubleshooting.LoggingTest logging
INFO: this is useful information

Python logs useful information at logger level — INFO

Example:

INFO     selenium:test_logging.py:22 this is useful information

.NET logs useful information at logger level Info.

Example:

11:04:40.986 INFO LoggingTest: this is useful information

Ruby logs useful information at logger level — :info.

Example:

2023-05-08 20:53:13 INFO Selenium [:example_id] this is useful information 

Logs useful information at level: INFO

Debugging Details

调试日志级别用于诊断问题和解决问题可能需要的信息.

Java日志的大多数调试信息在logger 级别 FINE

Example:

May 08, 2023 9:23:38 PM dev.selenium.troubleshooting.LoggingTest logging
FINE: this is detailed debug information

Python logs debugging details at logger level — DEBUG

Example:

DEBUG    selenium:test_logging.py:24 this is detailed debug information

.NET logs most debug content at logger level Debug.

Example:

11:04:40.986 DEBUG LoggingTest: this is detailed debug information

Ruby only provides one level for debugging, so all details are at logger level — :debug.

Example:

2023-05-08 20:53:13 DEBUG Selenium [:example_id] this is detailed debug information 

Logs debugging details at level: FINER and FINEST

Logger 输出

日志可以显示在控制台中, 也可以存储在文件中. 不同的语言有不同的默认值.

默认情况下, 所有日志都发送到 System.err. 要将输出定向到文件, 您需要添加一个处理程序:


      Path output = artifactsDir().resolve("selenium.xml");

By default all logs are sent to sys.stderr. To direct output somewhere else, you need to add a handler with either a StreamHandler or a FileHandler:

    handler = logging.FileHandler(log_path)
    logger.addHandler(handler)

By default all logs are sent to System.Console.Error output. To direct output somewhere else, you need to add a handler with a FileLogHandler:

By default, logs are sent to the console in stdout.
To store the logs in a file:

    logger.output = file_name

JavaScript does not currently support sending output to a file.

To send logs to console output:

logging.installConsoleHandler()

Logger 过滤

Java 日志记录是按类级别管理的, 因此不要使用根logger (Logger.getLogger("")), 而是在每个类的基础上设置要使用的级别:

        logger.addHandler(handler);
Because logging is managed by module, instead of working with just "selenium", you can specify different levels for different modules:
    logging.getLogger('selenium.webdriver.remote').setLevel(logging.WARN)
    logging.getLogger('selenium.webdriver.common').setLevel(logging.DEBUG)

.NET logging is managed on a per class level, set the level you want to use on a per-class basis:


            Log.SetLevel(typeof(RemoteWebDriver), LogEventLevel.Debug);

Ruby’s logger allows you to opt in (“allow”) or opt out (“ignore”) of log messages based on their IDs. Everything that Selenium logs includes an ID. You can also turn on or off all deprecation notices by using :deprecations.

These methods accept one or more symbols or an array of symbols:

    logger.ignore(:jwp_caps, :logger_info)

or

    logger.allow(%i[selenium_manager example_id])

3 - 升级到Selenium 4

对Selenium 4感兴趣? 查看本指南, 它将帮助您升级到最新版本!

如果您使用的是官方支持的语言 (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 标准.
对于大多数情况, 这种实现不会影响终端用户.
主要的例外是 Capabilities 和 Actions 类.

Capabilities

如果测试capabilities的结构不符合 W3C标准, 可能会导致会话无法正常开启.
以下是 W3C WebDriver 标准capabilities列表:

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

可以在以下位置找到标准capabilities的最新列表 W3C WebDriver.

上面列表中未包含的任何capability, 都需要包含供应商前缀. 这适用于浏览器特定capability 以及云供应商特定capability. 例如, 如果您的云供应商为您的测试 使用 build 和 name capability, 您需要将它们包装在一个 cloud: options 块中 (请与您的云供应商联系以获取适当的前缀).

Before

Move Code

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)

After

Move Code

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* 查找单个元素

Before
driver.findElementByClassName("className");
driver.findElementByCssSelector(".className");
driver.findElementById("elementId");
driver.findElementByLinkText("linkText");
driver.findElementByName("elementName");
driver.findElementByPartialLinkText("partialText");
driver.findElementByTagName("elementTagName");
driver.findElementByXPath("xPath");
After
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* 查找多个元素

Before
driver.findElementsByClassName("className");
driver.findElementsByCssSelector(".className");
driver.findElementsById("elementId");
driver.findElementsByLinkText("linkText");
driver.findElementsByName("elementName");
driver.findElementsByPartialLinkText("partialText");
driver.findElementsByTagName("elementTagName");
driver.findElementsByXPath("xPath");
After
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 中最常见的 Maven 和 Gradle .
所需的最低 Java 版本仍然是 8.

Maven

Before
<dependencies>
  <!-- more dependencies ... -->
  <dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.141.59</version>
  </dependency>
  <!-- more dependencies ... -->
</dependencies>
After
<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

Before
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()
}
After
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

Selenium 4 的更新细节 可以在RubyGems中的gem发现 selenium-webdriver .
要安装最新版本, 您可以执行:

gem install selenium-webdriver

将以下内容添加到你的Gemfile:

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

JavaScript

可以在 Node 包管理器中找到 selenium-webdriver 包, 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) .

Before
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.manage().timeouts().setScriptTimeout(2, TimeUnit.MINUTES);
driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);
After
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
driver.manage().timeouts().scriptTimeout(Duration.ofMinutes(2));
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(10));

等待现在也期望不同的参数. WebDriverWait 现在期待一个 Duration 而不是以秒和毫秒为单位的 long 超时. FluentWait 的工具方法 withTimeout 和 pollingEvery 已经从期望 (long time, TimeUnit unit) 切换到 期待(Duration duration) .

Before
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);
After
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);

合并capabilities不再改变调用对象

曾经可以将一组不同的capabilities合并到另一组中, 并且改变调用对象.
现在, 需要分配合并操作的结果.

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

//作为结果, `options` 对象被修改
After
MutableCapabilities capabilities = new MutableCapabilities();
capabilities.setCapability("platformVersion", "Windows 10");
FirefoxOptions options = new FirefoxOptions();
options.setHeadless(true);
options = options.merge(capabilities);

// `merge` 调用的结果需要分配给一个对象.

Firefox 遗留模式

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

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

BrowserType

BrowserType 接口已经存在很长时间了, 但是其已变为弃用 且推荐使用新的 Browser 接口.

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

C#

AddAdditionalCapability 已弃用

推荐使用AddAdditionalOption替代.
以下为一个示例:

Before
var browserOptions = new ChromeOptions();
browserOptions.PlatformName = "Windows 10";
browserOptions.BrowserVersion = "latest";
var cloudOptions = new Dictionary<string, object>();
browserOptions.AddAdditionalCapability("cloud: options", cloudOptions, true);
After
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 已弃用, 请传递一个服务对象

在Selenium 4中,
您需要从服务对象设置驱动程序的 可执行路径 , 以防止出现弃用警告. (或者不要设置路径, 而是确保所需的驱动程序位于系统路径上.)

Before
from selenium import webdriver
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(
    executable_path=CHROMEDRIVER_PATH, 
    options=options
)
After
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