Setup is done — now it is time to make the browser do something useful. In this lesson, you will write a complete Selenium test that opens a browser, navigates to a real web page, finds elements on the page, interacts with them (typing text, clicking buttons), and verifies the results. This is the core loop of every Selenium test: navigate, find, interact, assert. Every automated UI test you will ever write is a variation of this pattern.
The Navigate–Find–Interact–Assert Pattern
Every Selenium test follows a four-step pattern, regardless of complexity. Mastering this pattern with a simple example gives you the building block for testing any web application.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# ── Setup ──
driver = webdriver.Chrome()
driver.implicitly_wait(5) # Wait up to 5 seconds for elements to appear
try:
# ── Step 1: NAVIGATE ──
driver.get("https://www.saucedemo.com")
print(f"Navigated to: {driver.title}")
# ── Step 2: FIND elements ──
username_field = driver.find_element(By.ID, "user-name")
password_field = driver.find_element(By.ID, "password")
login_button = driver.find_element(By.ID, "login-button")
# ── Step 3: INTERACT with elements ──
username_field.clear()
username_field.send_keys("standard_user")
password_field.clear()
password_field.send_keys("secret_sauce")
login_button.click()
# ── Step 4: ASSERT the result ──
# Wait for the inventory page to load
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, "inventory_list"))
)
# Verify we landed on the correct page
assert "inventory" in driver.current_url, \
f"Expected inventory page, got {driver.current_url}"
# Verify products are displayed
products = driver.find_elements(By.CLASS_NAME, "inventory_item")
assert len(products) > 0, "No products found on inventory page"
print(f"Login successful! Found {len(products)} products.")
print(f"Current URL: {driver.current_url}")
# ── Bonus: interact with more elements ──
first_product_name = products[0].find_element(
By.CLASS_NAME, "inventory_item_name"
).text
print(f"First product: {first_product_name}")
# Add first product to cart
add_to_cart_btn = products[0].find_element(By.TAG_NAME, "button")
add_to_cart_btn.click()
# Verify cart badge shows "1"
cart_badge = driver.find_element(By.CLASS_NAME, "shopping_cart_badge")
assert cart_badge.text == "1", f"Expected cart badge '1', got '{cart_badge.text}'"
print("Product added to cart successfully!")
finally:
# ── Cleanup — always quit the browser ──
driver.quit()
print("Browser closed. Test complete.")
By class provides eight locator strategies: By.ID, By.NAME, By.CLASS_NAME, By.TAG_NAME, By.CSS_SELECTOR, By.XPATH, By.LINK_TEXT, and By.PARTIAL_LINK_TEXT. Of these, By.ID is the fastest and most reliable because IDs should be unique on the page. By.CSS_SELECTOR and By.XPATH are the most flexible for complex element targeting. Choosing the right locator strategy is critical for test stability — fragile locators are the number one cause of flaky tests, and the next chapter covers this in depth.find_elements (plural) when you expect multiple matches — it returns a list and never throws an exception if nothing is found (it returns an empty list). Use find_element (singular) when you expect exactly one match — it throws NoSuchElementException if the element is not found. This distinction is important: use plural for assertions like “verify at least 3 products are displayed” and singular for interactions like “click the login button.”time.sleep() as your primary wait strategy. Hard-coded sleeps slow down tests unnecessarily (sleeping 5 seconds when the element appeared in 0.5) and are still unreliable (the element might take 6 seconds on a slow CI server). Use WebDriverWait with expected conditions — it polls the DOM repeatedly until the condition is met or the timeout expires. This approach is both faster and more reliable than fixed sleeps.Common Mistakes
Mistake 1 — Using time.sleep() instead of explicit waits
❌ Wrong: time.sleep(5) after every action, making a 10-step test take 50+ seconds.
✅ Correct: WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "result"))) — waits only as long as needed, up to a maximum of 10 seconds.
Mistake 2 — Not wrapping tests in try/finally for cleanup
❌ Wrong: Writing driver.quit() at the end of the script — if an assertion fails on line 20, quit never runs and the browser stays open.
✅ Correct: Wrapping the test in a try/finally block (or using pytest fixtures) so driver.quit() executes regardless of whether the test passes or fails.