Locating Elements (CSS, XPath, ID)
Finding elements on a web page is the foundation of all Selenium tests. The quality of your locators determines whether your tests are robust and maintainable or fragile and constantly breaking. Bad locators are the #1 cause of flaky automation tests. This topic teaches you to write locators that survive UI changes.
Locator Strategy Priority (Best to Worst)
- 1. ID (best): driver.find_element(By.ID, 'submit-btn') — unique, fast, stable when semantically named
- 2. data-testid attribute: driver.find_element(By.CSS_SELECTOR, '[data-testid="login-button"]') — automation-specific, survives CSS changes
- 3. Name: driver.find_element(By.NAME, 'username') — good for form fields
- 4. CSS Selector: driver.find_element(By.CSS_SELECTOR, '.btn-primary[type="submit"]') — flexible, readable, fast
- 5. XPath: driver.find_element(By.XPATH, '//button[text()="Sign In"]') — most powerful but slowest; use only when CSS can't reach it
- 6. Link Text: driver.find_element(By.LINK_TEXT, 'Forgot Password') — for anchor elements only
- AVOID: Absolute XPaths like /html/body/div[3]/div[1]/button[2] — breaks on any HTML restructuring
Locator Mastery: CSS and XPath
from selenium.webdriver.common.by import By
# ══════════════════════════════════════════════════════════════
# CSS SELECTORS — Preferred for most cases
# ══════════════════════════════════════════════════════════════
# By ID
driver.find_element(By.CSS_SELECTOR, "#submit-btn")
# By class
driver.find_element(By.CSS_SELECTOR, ".login-form")
# By attribute (best practice: data-testid)
driver.find_element(By.CSS_SELECTOR, '[data-testid="email-input"]')
# By tag + attribute
driver.find_element(By.CSS_SELECTOR, 'input[type="email"]')
# By parent → child
driver.find_element(By.CSS_SELECTOR, '.checkout-form input[name="card-number"]')
# Nth child
driver.find_element(By.CSS_SELECTOR, '.product-list .product-card:nth-child(2)')
# ══════════════════════════════════════════════════════════════
# XPATH — Use when CSS can't do it (parent traversal, text match)
# ══════════════════════════════════════════════════════════════
# By exact text
driver.find_element(By.XPATH, '//button[text()="Add to Cart"]')
# By partial text
driver.find_element(By.XPATH, '//button[contains(text(), "Add")]')
# By attribute
driver.find_element(By.XPATH, '//input[@placeholder="Enter email"]')
# Parent element (CSS cannot do this)
# Find the row containing "iPhone" and then find its price cell
driver.find_element(By.XPATH, '//tr[td[text()="iPhone"]]/td[@class="price"]')
# Multiple attribute conditions
driver.find_element(By.XPATH, '//input[@type="text" and @class="search-box"]')
# ══════════════════════════════════════════════════════════════
# FINDING MULTIPLE ELEMENTS
# ══════════════════════════════════════════════════════════════
products = driver.find_elements(By.CSS_SELECTOR, '.product-card') # Returns list
print(f"Found {len(products)} products")
for product in products:
name = product.find_element(By.CSS_SELECTOR, '.product-name').text
price = product.find_element(By.CSS_SELECTOR, '.price').text
print(f"{name}: {price}")Common Mistakes
- Using absolute XPath — /html/body/div[3]/form/input[1] breaks on any HTML change; always use relative XPath or CSS
- Relying on generated class names — React/Angular often generate random classes like 'sc-1a2b3c'; always request data-testid attributes from developers
- Using text-based locators in multilingual apps — '//button[text()="Login"]' breaks when language is French; use data-testid instead
- Not verifying locators in the browser — always test your locator in the browser console ($('[data-testid="btn"]')) before adding it to code
Tip
Tip
Practice Locating Elements CSS XPath ID in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
:: = content generation. : = state selection.
Practice Task
Note
Practice Task — (1) Write a working example of Locating Elements CSS XPath ID from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Locating Elements CSS XPath ID is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready software testing code.
Key Takeaways
- Finding elements on a web page is the foundation of all Selenium tests.
- 1. ID (best): driver.find_element(By.ID, 'submit-btn') — unique, fast, stable when semantically named
- 2. data-testid attribute: driver.find_element(By.CSS_SELECTOR, '[data-testid="login-button"]') — automation-specific, survives CSS changes
- 3. Name: driver.find_element(By.NAME, 'username') — good for form fields