Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
---
title: "Outdated Selenium patterns and their current replacements"
linkTitle: "Outdated Patterns"
weight: 2
description: >-
Selenium 2 and 3 idioms that coding agents and old tutorials still produce, with the
current form of each.
---

Coding agents and old tutorials reproduce Selenium 2 and Selenium 3 idioms,
because that is what most of the published material describes.
Each section below shows one outdated pattern,
when it stopped being the right way to write Selenium,
why, and the current form.
The outdated form is always marked with an `Outdated — do not use` comment.

This page is the reference behind the rules in
[Using AI coding agents with Selenium]({{< ref "/documentation/ai_agents/" >}}).
Use it to recognise bad output in a review.

## DesiredCapabilities

**Outdated since Selenium 4**, which uses the W3C WebDriver standard by default and removes
support for the legacy protocol.
Java Grid also stopped accepting session requests that use the `desiredCapabilities` keyword in 4.17.

Free-form capability maps predate the W3C standard.
Non-standard capabilities now have to be vendor-prefixed,
and an unprefixed one can stop the session from starting.
The browser Options classes produce a valid payload by construction.
See the [upgrade guide]({{< ref "/documentation/webdriver/troubleshooting/upgrade_to_selenium_4.md" >}})
and [Browser options]({{< ref "/documentation/webdriver/drivers/options.md" >}}).

{{< tabpane text=true >}}
{{% tab header="Java" %}}
```java
// Outdated — do not use
DesiredCapabilities caps = DesiredCapabilities.chrome();
caps.setCapability("pageLoadStrategy", "eager");
WebDriver driver = new ChromeDriver(caps);
```
{{% /tab %}}
{{% tab header="Python" %}}
```python
# Outdated — do not use
caps = DesiredCapabilities.CHROME.copy()
caps["pageLoadStrategy"] = "eager"
driver = webdriver.Chrome(desired_capabilities=caps)
```
{{% /tab %}}
{{< /tabpane >}}

Current form:

{{< tabpane text=true >}}
{{< tab header="Java" >}}
{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/drivers/OptionsTest.java#L22-L24" >}}
{{< /tab >}}
{{< tab header="Python" >}}
{{< gh-codeblock path="/examples/python/tests/drivers/test_options.py#L7-L9" >}}
{{< /tab >}}
{{< /tabpane >}}

## Manual driver downloads and driver managers

**Superseded in Selenium 4.6**, when Selenium Manager began shipping with every Selenium release.
Since 4.11 Selenium Manager can also download the browser.

Setting a path to `chromedriver`, putting a driver on the `PATH` by hand,
or adding a third-party driver manager adds a second, slower source of truth
for which driver version to use.
There is nothing to write:
start the driver and Selenium Manager finds or downloads the matching one.
See [Selenium Manager]({{< ref "/documentation/selenium_manager.md" >}}).

{{< tabpane text=true >}}
{{% tab header="Java" %}}
```java
// Outdated — do not use
WebDriverManager.chromedriver().setup();
System.setProperty("webdriver.chrome.driver", "/path/to/chromedriver");
WebDriver driver = new ChromeDriver();
```
{{% /tab %}}
{{% tab header="Python" %}}
```python
# Outdated — do not use
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(ChromeDriverManager().install())
```
{{% /tab %}}
{{< /tabpane >}}

Current form:

{{< tabpane text=true >}}
{{< tab header="Java" >}}
{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/selenium_manager/SeleniumManagerUsageDemo.java#L21" >}}
{{< /tab >}}
{{< tab header="Python" >}}
{{< gh-codeblock path="/examples/python/tests/selenium_manager/usage.py#L11" >}}
{{< /tab >}}
{{< /tabpane >}}

## sleep()

**Never good practice.**
Fixed sleeps were bad advice before Selenium 4 and are bad advice now.

A fixed sleep is either too short, and the test fails,
or too long, and the suite crawls.
Wait for the condition the next line depends on instead.
See [Waiting strategies]({{< ref "/documentation/webdriver/waits.md" >}}).

{{< tabpane text=true >}}
{{% tab header="Java" %}}
```java
// Outdated — do not use
driver.findElement(By.id("adder")).click();
Thread.sleep(5000);
WebElement added = driver.findElement(By.id("box0"));
```
{{% /tab %}}
{{% tab header="Python" %}}
```python
# Outdated — do not use
driver.find_element(By.ID, "adder").click()
time.sleep(5)
added = driver.find_element(By.ID, "box0")
```
{{% /tab %}}
{{< /tabpane >}}

Current form:

{{< tabpane text=true >}}
{{< tab header="Java" >}}
{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/waits/WaitsTest.java#L67-L68" >}}
{{< /tab >}}
{{< tab header="Python" >}}
{{< gh-codeblock path="/examples/python/tests/waits/test_waits.py#L41-L42" >}}
{{< /tab >}}
{{< /tabpane >}}

## Absolute XPath copied from DevTools

**Never good practice.**
An absolute path describes where an element sits in today's markup,
so it breaks on the next front-end change.

Prefer an `id` or `name`, then a CSS selector on a stable attribute.
When an element has no usable locator but is easy to describe by position,
use a relative locator.
See [Locator strategies]({{< ref "/documentation/webdriver/elements/locators.md" >}}).

{{< tabpane text=true >}}
{{% tab header="Java" %}}
```java
// Outdated — do not use
driver.findElement(By.xpath("/html/body/div[2]/div[1]/form/div[3]/input"));
```
{{% /tab %}}
{{% tab header="Python" %}}
```python
# Outdated — do not use
driver.find_element(By.XPATH, "/html/body/div[2]/div[1]/form/div[3]/input")
```
{{% /tab %}}
{{< /tabpane >}}

Current form, with a stable locator:

{{< tabpane text=true >}}
{{< tab header="Java" >}}
{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/elements/LocatorTest.java#L48" >}}
{{< /tab >}}
{{< tab header="Python" >}}
{{< gh-codeblock path="/examples/python/tests/elements/test_locators.py#L28" >}}
{{< /tab >}}
{{< /tabpane >}}

Current form, with a relative locator:

{{< tabpane text=true >}}
{{% tab header="Java" %}}
```java
By passwordLocator = RelativeLocator.with(By.tagName("input")).below(By.id("email"));
```
{{% /tab %}}
{{% tab header="Python" %}}
```python
password_locator = locate_with(By.TAG_NAME, "input").below({By.ID: "email"})
```
{{% /tab %}}
{{< /tabpane >}}

See [Relative locators]({{< ref "/documentation/webdriver/elements/locators.md#relative-locators" >}})
for the full set of relative locator methods.

## find_element_by_* methods

**Superseded in Selenium 4.**
The Python bindings announced the removal of `find_element_by_*` and `find_elements_by_*`
after Selenium 4.2.
The Java `findElementBy*` helpers were removed in Selenium 4.

Every one of them is a special case of `find_element` with a `By` strategy.
See the
[Python locators blog post]({{< ref "/blog/2022/python-locators-se4.md" >}})
and the [upgrade guide]({{< ref "/documentation/webdriver/troubleshooting/upgrade_to_selenium_4.md" >}}).

{{< tabpane text=true >}}
{{% tab header="Java" %}}
```java
// Outdated — do not use
driver.findElementById("lname");
```
{{% /tab %}}
{{% tab header="Python" %}}
```python
# Outdated — do not use
driver.find_element_by_id("lname")
```
{{% /tab %}}
{{< /tabpane >}}

Current form:

{{< tabpane text=true >}}
{{< tab header="Java" >}}
{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/elements/LocatorTest.java#L48" >}}
{{< /tab >}}
{{< tab header="Python" >}}
{{< gh-codeblock path="/examples/python/tests/elements/test_locators.py#L28" >}}
{{< /tab >}}
{{< /tabpane >}}

## executable_path

**Deprecated in Selenium 4** in favour of a `Service` object.

In Python, the `executable_path` argument to the driver constructor is the outdated form.
A path belongs on a `Service`.
Most projects do not need one at all, because Selenium Manager finds the driver;
see [driver management](#manual-driver-downloads-and-driver-managers) above.
See the [upgrade guide]({{< ref "/documentation/webdriver/troubleshooting/upgrade_to_selenium_4.md" >}})
and [Driver service]({{< ref "/documentation/webdriver/drivers/service.md" >}}).

```python
# Outdated — do not use
driver = webdriver.Chrome(executable_path="/path/to/chromedriver", options=options)
```

Current form, when a specific driver binary is really needed:

{{< tabpane text=true >}}
{{< tab header="Python" >}}
{{< gh-codeblock path="/examples/python/tests/drivers/test_service.py#L15-L17" >}}
{{< /tab >}}
{{< /tabpane >}}

## CDP-only calls

**Temporary by design.**
The [CDP pages]({{< ref "/documentation/webdriver/bidi/cdp/" >}}) describe Selenium's Chrome DevTools Protocol
support as temporary until WebDriver BiDi is implemented.
Firefox CDP support was deprecated in 4.27 and 4.28 and fully removed in 4.29.

The Chrome DevTools Protocol is Chromium-only and has no stable API between browser versions.
[WebDriver BiDi]({{< ref "/documentation/webdriver/bidi/w3c/" >}}) is the W3C standard that replaces it,
and it works across browsers.

{{< tabpane text=true >}}
{{% tab header="Java" %}}
```java
// Outdated — do not use
DevTools devTools = ((HasDevTools) driver).getDevTools();
devTools.createSession();
devTools.send(Log.enable());
devTools.addListener(Log.entryAdded(), entry -> System.out.println(entry.getText()));
```
{{% /tab %}}
{{% tab header="Python" %}}
```python
# Outdated — do not use
driver.execute_cdp_cmd("Log.enable", {})
```
{{% /tab %}}
{{< /tabpane >}}

Current form, listening to console logs with BiDi:

{{< tabpane text=true >}}
{{< tab header="Java" >}}
{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/LogTest.java#L30-L39" >}}
{{< /tab >}}
{{< tab header="Python" >}}
{{< gh-codeblock path="/examples/python/tests/bidi/test_bidi_logging.py#L6-L15" >}}
{{< /tab >}}
{{< /tabpane >}}
Loading