Every Selenium test follows the same pattern: navigate to a page, find an element, interact with it, and verify the result. The “find an element” step is where most tests break. If your locator is fragile — tied to a CSS class that a developer renames, or an XPath that depends on exact DOM position — your test fails even though the application works perfectly. Choosing the right locator strategy is the single most important skill for building stable, maintainable Selenium tests.
The Eight Locator Strategies — Speed, Reliability and Trade-offs
Selenium provides eight ways to find elements on a page. Each has different strengths, and knowing when to use which one prevents the locator-related flakiness that plagues many automation projects.
from selenium.webdriver.common.by import By
# ── The 8 Selenium locator strategies ──
LOCATOR_STRATEGIES = [
{
"strategy": "By.ID",
"example": 'driver.find_element(By.ID, "login-button")',
"html": '<button id="login-button">Sign In</button>',
"speed": "Fastest",
"reliability": "Highest — IDs should be unique per page",
"when_to_use": "Always the first choice when an ID is available",
"limitation": "Not every element has an ID; some frameworks generate dynamic IDs",
},
{
"strategy": "By.NAME",
"example": 'driver.find_element(By.NAME, "username")',
"html": '<input name="username" type="text">',
"speed": "Fast",
"reliability": "High — names are stable in form elements",
"when_to_use": "Form fields (input, select, textarea) that have name attributes",
"limitation": "Names are not guaranteed unique; multiple forms may reuse names",
},
{
"strategy": "By.CSS_SELECTOR",
"example": 'driver.find_element(By.CSS_SELECTOR, "form.login input[type=\'email\']")',
"html": '<form class="login"><input type="email"></form>',
"speed": "Fast",
"reliability": "High when using stable attributes",
"when_to_use": "Complex targeting: attribute combinations, hierarchy, nth-child",
"limitation": "Cannot traverse upward (parent) or search by text content",
},
{
"strategy": "By.XPATH",
"example": 'driver.find_element(By.XPATH, "//button[text()=\'Submit\']")',
"html": '<button>Submit</button>',
"speed": "Slower than CSS",
"reliability": "Medium — powerful but easy to write fragile paths",
"when_to_use": "Text-based search, parent traversal, complex conditions",
"limitation": "Verbose syntax; position-dependent XPaths are very fragile",
},
{
"strategy": "By.CLASS_NAME",
"example": 'driver.find_element(By.CLASS_NAME, "btn-primary")',
"html": '<button class="btn btn-primary">Save</button>',
"speed": "Fast",
"reliability": "Low — classes are shared, change often for styling reasons",
"when_to_use": "Only when the class is semantically meaningful and unique",
"limitation": "Only matches a single class name; cannot use compound selectors",
},
{
"strategy": "By.TAG_NAME",
"example": 'driver.find_elements(By.TAG_NAME, "li")',
"html": '<ul><li>Item 1</li><li>Item 2</li></ul>',
"speed": "Fast",
"reliability": "Low — returns all matching tags on the page",
"when_to_use": "Counting elements or scoping within a parent container",
"limitation": "Almost never unique; always returns multiple results",
},
{
"strategy": "By.LINK_TEXT",
"example": 'driver.find_element(By.LINK_TEXT, "Forgot Password?")',
"html": '<a href="/reset">Forgot Password?</a>',
"speed": "Fast",
"reliability": "Medium — breaks if link text changes or is translated",
"when_to_use": "Navigational links with stable, unique text",
"limitation": "Exact match only; breaks with internationalisation",
},
{
"strategy": "By.PARTIAL_LINK_TEXT",
"example": 'driver.find_element(By.PARTIAL_LINK_TEXT, "Forgot")',
"html": '<a href="/reset">Forgot Password?</a>',
"speed": "Fast",
"reliability": "Low — partial matches can be ambiguous",
"when_to_use": "Links with dynamic suffixes but stable prefixes",
"limitation": "May match unintended links if the partial text is too generic",
},
]
# Priority hierarchy
LOCATOR_PRIORITY = [
"1. By.ID — unique, fast, stable (first choice)",
"2. By.NAME — great for form fields",
"3. By.CSS_SELECTOR— flexible, readable, fast",
"4. By.XPATH — when CSS cannot do it (text search, parent traversal)",
"5. By.LINK_TEXT — only for stable navigation links",
"6. By.CLASS_NAME — only if semantically meaningful",
"7. By.TAG_NAME — only for counting or scoping",
"8. By.PARTIAL_LINK_TEXT — last resort",
]
print("Selenium Locator Strategies — Priority Hierarchy")
print("=" * 60)
for p in LOCATOR_PRIORITY:
print(f" {p}")
print(f"\n\nDetailed Comparison:")
for loc in LOCATOR_STRATEGIES:
print(f"\n {loc['strategy']}")
print(f" Speed: {loc['speed']}")
print(f" Reliability: {loc['reliability']}")
print(f" Use when: {loc['when_to_use']}")
print(f" Limitation: {loc['limitation']}")
data-testid attribute. This is a widely accepted practice: developers add attributes like data-testid="submit-order" specifically for test automation. These attributes are immune to CSS refactoring, invisible to users, and signal their purpose clearly. Many teams adopt a convention where every interactive element gets a data-testid, making automation locators trivial and rock-solid.mat-input-3, ctl00_ContentPlaceHolder1_TextBox1, or ember-432. These IDs change between page loads, builds, or when components are reordered. If the ID looks like it was generated by a framework rather than written by a human, treat it as unstable and use a CSS selector or data attribute instead.Common Mistakes
Mistake 1 — Using XPath for everything because it is the most powerful
❌ Wrong: driver.find_element(By.XPATH, "/html/body/div[3]/form/div[2]/input") — an absolute XPath that breaks when any ancestor element changes.
✅ Correct: driver.find_element(By.ID, "email") or driver.find_element(By.CSS_SELECTOR, "form.login input[type='email']") — simpler, faster, and more resilient to DOM changes.
Mistake 2 — Using class names that are purely visual styling
❌ Wrong: driver.find_element(By.CLASS_NAME, "mt-4") — a Tailwind CSS spacing class that has no semantic meaning and could be changed by any designer.
✅ Correct: driver.find_element(By.CSS_SELECTOR, "[data-testid='login-form']") — a purpose-built test attribute that is stable and meaningful.