
Selenium with Python Tutorial
Python's simplicity makes it ideal for test automation. With readable syntax, a gentle learning curve, and powerful libraries like pytest, Python enables rapid development of maintainable test frameworks. Whether you're new to automation or experienced with other languages, this guide covers everything you need to build professional Selenium tests with Python.
Python's "batteries included" philosophy extends to testing - you'll be writing effective browser automation in minutes.
Table Of Contents-
Prerequisites and Setup
Required Software
| Software | Version | Purpose |
|---|---|---|
| Python | 3.8+ | Runtime |
| pip | Latest | Package manager |
| Browser | Chrome, Firefox, Edge | Test execution |
| IDE | VS Code, PyCharm | Development |
Verify Python Installation
# Check Python version
python --version
# or
python3 --version
# Check pip
pip --version
# or
pip3 --versionIf not installed, download from python.org (opens in a new tab). During installation on Windows, check "Add Python to PATH".
Create Virtual Environment (Recommended)
# Create virtual environment
python -m venv venv
# Activate (Windows)
venv\Scripts\activate
# Activate (macOS/Linux)
source venv/bin/activateInstallation
Install Selenium
pip install seleniumInstall WebDriver Manager
WebDriver Manager automatically handles browser driver downloads:
pip install webdriver-managerInstall pytest
pip install pytest
pip install pytest-html # For HTML reportsFull Requirements File
Create requirements.txt:
selenium==4.21.0
webdriver-manager==4.0.1
pytest==8.1.1
pytest-html==4.1.1
pytest-xdist==3.5.0 # For parallel executionInstall all dependencies:
pip install -r requirements.txtProject Structure
selenium-tests/
├── pages/ # Page Objects
│ ├── __init__.py
│ ├── base_page.py
│ └── login_page.py
├── tests/ # Test files
│ ├── __init__.py
│ ├── conftest.py # pytest fixtures
│ └── test_login.py
├── utils/ # Utilities
│ ├── __init__.py
│ └── config.py
├── requirements.txt
├── pytest.ini
└── README.mdYour First Selenium Test
Simple Script
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
# Setup Chrome driver
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
try:
# Navigate to URL
driver.get("https://www.google.com")
# Find search box and enter text
search_box = driver.find_element(By.NAME, "q")
search_box.send_keys("Selenium Python")
search_box.submit()
# Print page title
print(f"Page title: {driver.title}")
finally:
# Close browser
driver.quit()Test with pytest
# test_google.py
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
@pytest.fixture
def driver():
"""Setup and teardown for each test."""
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.maximize_window()
yield driver
driver.quit()
def test_google_search(driver):
"""Test Google search functionality."""
driver.get("https://www.google.com")
search_box = driver.find_element(By.NAME, "q")
search_box.send_keys("Selenium Python")
search_box.submit()
assert "Selenium Python" in driver.titleRun the test:
pytest test_google.py -vWebDriver Basics
Creating Driver Instances
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.firefox import GeckoDriverManager
from webdriver_manager.microsoft import EdgeChromiumDriverManager
# Chrome
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
# Firefox
driver = webdriver.Firefox(service=Service(GeckoDriverManager().install()))
# Edge
driver = webdriver.Edge(service=Service(EdgeChromiumDriverManager().install()))Browser Options
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless") # Headless mode
options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920,1080")
options.add_argument("--incognito") # Incognito mode
options.add_argument("--disable-notifications")
options.add_argument("--disable-extensions")
driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)Navigation Commands
# Navigate to URL
driver.get("https://example.com")
# Browser navigation
driver.back()
driver.forward()
driver.refresh()
# Get page info
title = driver.title
url = driver.current_url
source = driver.page_sourceWindow Management
# Maximize window
driver.maximize_window()
# Set specific size
driver.set_window_size(1920, 1080)
# Full screen
driver.fullscreen_window()
# Get window size
size = driver.get_window_size()
print(f"Width: {size['width']}, Height: {size['height']}")Locator Strategies
The By Class
from selenium.webdriver.common.by import By
# By ID
driver.find_element(By.ID, "username")
# By Name
driver.find_element(By.NAME, "email")
# By Class Name (single class)
driver.find_element(By.CLASS_NAME, "btn-primary")
# By Tag Name
driver.find_elements(By.TAG_NAME, "input")
# By Link Text (exact match)
driver.find_element(By.LINK_TEXT, "Sign In")
# By Partial Link Text
driver.find_element(By.PARTIAL_LINK_TEXT, "Sign")
# By CSS Selector (recommended)
driver.find_element(By.CSS_SELECTOR, "#login-form input[type='email']")
driver.find_element(By.CSS_SELECTOR, ".btn.btn-primary")
driver.find_element(By.CSS_SELECTOR, "[data-testid='submit-btn']")
# By XPath (when CSS can't do it)
driver.find_element(By.XPATH, "//button[text()='Submit']")
driver.find_element(By.XPATH, "//input[@id='email']/../label")CSS Selector Examples
# ID
By.CSS_SELECTOR, "#elementId"
# Class
By.CSS_SELECTOR, ".className"
# Attribute
By.CSS_SELECTOR, "[name='email']"
By.CSS_SELECTOR, "input[type='text']"
# Partial attribute match
By.CSS_SELECTOR, "[id*='partial']" # Contains
By.CSS_SELECTOR, "[id^='start']" # Starts with
By.CSS_SELECTOR, "[id$='end']" # Ends with
# Hierarchy
By.CSS_SELECTOR, "form > input" # Direct child
By.CSS_SELECTOR, "form input" # Any descendant
By.CSS_SELECTOR, "label + input" # Adjacent sibling
# Pseudo-classes
By.CSS_SELECTOR, "tr:nth-child(2)" # Second row
By.CSS_SELECTOR, "li:first-child"
By.CSS_SELECTOR, "li:last-child"Finding Multiple Elements
# Find all links
links = driver.find_elements(By.TAG_NAME, "a")
print(f"Found {len(links)} links")
# Iterate through elements
for link in links:
print(f"{link.text} -> {link.get_attribute('href')}")
# Find elements within element
container = driver.find_element(By.ID, "nav")
nav_links = container.find_elements(By.TAG_NAME, "a")Element Interactions
Basic Interactions
element = driver.find_element(By.ID, "username")
# Click
element.click()
# Type text
element.send_keys("test@example.com")
# Clear field
element.clear()
# Submit form
element.submit()
# Get text content
text = element.text
# Get attribute value
value = element.get_attribute("value")
placeholder = element.get_attribute("placeholder")
# Check element state
is_displayed = element.is_displayed()
is_enabled = element.is_enabled()
is_selected = element.is_selected()Keyboard Actions
from selenium.webdriver.common.keys import Keys
input_field = driver.find_element(By.ID, "search")
# Special keys
input_field.send_keys(Keys.ENTER)
input_field.send_keys(Keys.TAB)
input_field.send_keys(Keys.ESCAPE)
# Key combinations
input_field.send_keys(Keys.CONTROL + "a") # Select all
input_field.send_keys(Keys.CONTROL + "c") # Copy
# Clear and type
input_field.send_keys(Keys.CONTROL + "a", Keys.DELETE)
input_field.send_keys("new text")Dropdown Selection
from selenium.webdriver.support.ui import Select
dropdown = driver.find_element(By.ID, "country")
select = Select(dropdown)
# Select by visible text
select.select_by_visible_text("United States")
# Select by value attribute
select.select_by_value("US")
# Select by index (0-based)
select.select_by_index(2)
# Get selected option
selected = select.first_selected_option
print(f"Selected: {selected.text}")
# Get all options
options = select.options
for option in options:
print(option.text)
# Multi-select dropdowns
if select.is_multiple:
select.deselect_all()
select.select_by_visible_text("Option 1")
select.select_by_visible_text("Option 2")Checkboxes and Radio Buttons
# Checkbox
checkbox = driver.find_element(By.ID, "remember-me")
if not checkbox.is_selected():
checkbox.click()
# Radio button
radio = driver.find_element(By.CSS_SELECTOR, "input[value='option1']")
radio.click()
# Verify selection
assert checkbox.is_selected()Handling Waits
Explicit Wait (Recommended)
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
# Wait for element to be visible
element = wait.until(
EC.visibility_of_element_located((By.ID, "result"))
)
# Wait for element to be clickable
button = wait.until(
EC.element_to_be_clickable((By.ID, "submit"))
)
button.click()
# Wait for text to appear
wait.until(
EC.text_to_be_present_in_element((By.ID, "status"), "Complete")
)
# Wait for element to disappear
wait.until(
EC.invisibility_of_element_located((By.ID, "loader"))
)
# Wait for URL to change
wait.until(EC.url_contains("/dashboard"))Common Expected Conditions
from selenium.webdriver.support import expected_conditions as EC
# Presence (in DOM, may be hidden)
EC.presence_of_element_located((By.ID, "element"))
# Visibility (in DOM and visible)
EC.visibility_of_element_located((By.ID, "element"))
# Clickable (visible and enabled)
EC.element_to_be_clickable((By.ID, "element"))
# Text present
EC.text_to_be_present_in_element((By.ID, "element"), "expected text")
# Attribute value
EC.element_attribute_to_include((By.ID, "element"), "class")
# Frame available
EC.frame_to_be_available_and_switch_to_it((By.ID, "frame"))
# Alert present
EC.alert_is_present()
# Title
EC.title_contains("Dashboard")
# URL
EC.url_contains("/login")Custom Wait Condition
# Using lambda
wait.until(lambda driver: driver.find_element(By.ID, "status").text == "Ready")
# Custom condition function
def element_has_class(locator, class_name):
def _predicate(driver):
element = driver.find_element(*locator)
classes = element.get_attribute("class")
return class_name in classes if classes else False
return _predicate
wait.until(element_has_class((By.ID, "btn"), "active"))Implicit Wait (Use Sparingly)
# Set once - applies to all find_element calls
driver.implicitly_wait(10)
# Note: Don't mix implicit and explicit waitspytest Integration
Fixtures in conftest.py
# conftest.py
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
@pytest.fixture(scope="function")
def driver():
"""Create a new browser instance for each test."""
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.maximize_window()
driver.implicitly_wait(5)
yield driver
driver.quit()
@pytest.fixture(scope="session")
def base_url():
"""Base URL for all tests."""
return "https://example.com"
@pytest.fixture
def logged_in_driver(driver, base_url):
"""Driver with user already logged in."""
driver.get(f"{base_url}/login")
driver.find_element(By.ID, "username").send_keys("testuser")
driver.find_element(By.ID, "password").send_keys("password123")
driver.find_element(By.ID, "login-btn").click()
yield driverpytest Configuration
# pytest.ini
[pytest]
testpaths = tests
python_files = test_*.py
python_functions = test_*
addopts = -v --html=reports/report.html --self-contained-html
markers =
smoke: Quick smoke tests
regression: Full regression tests
slow: Tests that take longer to runTest File Structure
# tests/test_login.py
import pytest
from selenium.webdriver.common.by import By
class TestLogin:
"""Test suite for login functionality."""
def test_valid_login(self, driver, base_url):
"""Test login with valid credentials."""
driver.get(f"{base_url}/login")
driver.find_element(By.ID, "username").send_keys("valid@test.com")
driver.find_element(By.ID, "password").send_keys("password123")
driver.find_element(By.ID, "login-btn").click()
assert "Dashboard" in driver.title
def test_invalid_login(self, driver, base_url):
"""Test login with invalid credentials."""
driver.get(f"{base_url}/login")
driver.find_element(By.ID, "username").send_keys("invalid@test.com")
driver.find_element(By.ID, "password").send_keys("wrongpassword")
driver.find_element(By.ID, "login-btn").click()
error = driver.find_element(By.CLASS_NAME, "error-message")
assert error.is_displayed()
assert "Invalid credentials" in error.text
@pytest.mark.smoke
def test_login_page_elements(self, driver, base_url):
"""Smoke test: verify login page elements exist."""
driver.get(f"{base_url}/login")
assert driver.find_element(By.ID, "username").is_displayed()
assert driver.find_element(By.ID, "password").is_displayed()
assert driver.find_element(By.ID, "login-btn").is_displayed()Parametrized Tests
import pytest
@pytest.mark.parametrize("username,password,expected", [
("valid@test.com", "password123", True),
("invalid@test.com", "wrong", False),
("", "password", False),
("user@test.com", "", False),
])
def test_login_scenarios(driver, base_url, username, password, expected):
"""Test various login scenarios."""
driver.get(f"{base_url}/login")
if username:
driver.find_element(By.ID, "username").send_keys(username)
if password:
driver.find_element(By.ID, "password").send_keys(password)
driver.find_element(By.ID, "login-btn").click()
if expected:
assert "Dashboard" in driver.title
else:
assert driver.find_element(By.CLASS_NAME, "error-message").is_displayed()Running Tests
# Run all tests
pytest
# Run specific file
pytest tests/test_login.py
# Run specific test
pytest tests/test_login.py::TestLogin::test_valid_login
# Run by marker
pytest -m smoke
pytest -m "not slow"
# Run with verbose output
pytest -v
# Run in parallel (requires pytest-xdist)
pytest -n 4
# Generate HTML report
pytest --html=report.htmlPage Object Model
Base Page Class
# pages/base_page.py
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class BasePage:
"""Base class for all page objects."""
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def find_element(self, locator):
"""Find element with explicit wait."""
return self.wait.until(EC.visibility_of_element_located(locator))
def find_elements(self, locator):
"""Find multiple elements."""
return self.driver.find_elements(*locator)
def click(self, locator):
"""Click element after waiting for it to be clickable."""
element = self.wait.until(EC.element_to_be_clickable(locator))
element.click()
def type_text(self, locator, text):
"""Clear and type text into element."""
element = self.find_element(locator)
element.clear()
element.send_keys(text)
def get_text(self, locator):
"""Get text from element."""
return self.find_element(locator).text
def is_element_visible(self, locator, timeout=5):
"""Check if element is visible."""
try:
WebDriverWait(self.driver, timeout).until(
EC.visibility_of_element_located(locator)
)
return True
except:
return False
def wait_for_url_contains(self, text):
"""Wait for URL to contain text."""
self.wait.until(EC.url_contains(text))Login Page Object
# pages/login_page.py
from selenium.webdriver.common.by import By
from pages.base_page import BasePage
class LoginPage(BasePage):
"""Page object for login page."""
# Locators
USERNAME_INPUT = (By.ID, "username")
PASSWORD_INPUT = (By.ID, "password")
LOGIN_BUTTON = (By.ID, "login-btn")
ERROR_MESSAGE = (By.CLASS_NAME, "error-message")
REMEMBER_ME = (By.ID, "remember-me")
def __init__(self, driver):
super().__init__(driver)
self.url = "/login"
def open(self, base_url):
"""Navigate to login page."""
self.driver.get(f"{base_url}{self.url}")
return self
def enter_username(self, username):
"""Enter username."""
self.type_text(self.USERNAME_INPUT, username)
return self
def enter_password(self, password):
"""Enter password."""
self.type_text(self.PASSWORD_INPUT, password)
return self
def click_login(self):
"""Click login button."""
self.click(self.LOGIN_BUTTON)
return self
def login(self, username, password):
"""Perform complete login."""
self.enter_username(username)
self.enter_password(password)
self.click_login()
return self
def get_error_message(self):
"""Get error message text."""
return self.get_text(self.ERROR_MESSAGE)
def is_error_displayed(self):
"""Check if error message is visible."""
return self.is_element_visible(self.ERROR_MESSAGE)
def check_remember_me(self):
"""Check remember me checkbox."""
checkbox = self.find_element(self.REMEMBER_ME)
if not checkbox.is_selected():
checkbox.click()
return selfTests Using Page Objects
# tests/test_login.py
import pytest
from pages.login_page import LoginPage
class TestLogin:
"""Test suite for login functionality."""
@pytest.fixture(autouse=True)
def setup(self, driver, base_url):
"""Setup for each test."""
self.login_page = LoginPage(driver)
self.login_page.open(base_url)
def test_valid_login(self, driver):
"""Test login with valid credentials."""
self.login_page.login("valid@test.com", "password123")
assert "Dashboard" in driver.title
def test_invalid_login(self):
"""Test login with invalid credentials."""
self.login_page.login("invalid@test.com", "wrongpassword")
assert self.login_page.is_error_displayed()
assert "Invalid credentials" in self.login_page.get_error_message()
def test_empty_username(self):
"""Test login with empty username."""
self.login_page.enter_password("password123")
self.login_page.click_login()
assert self.login_page.is_error_displayed()
def test_remember_me(self, driver):
"""Test remember me functionality."""
self.login_page.check_remember_me()
self.login_page.login("valid@test.com", "password123")
# Verify login succeeded
assert "Dashboard" in driver.titleAdvanced Techniques
Handling Alerts
from selenium.webdriver.common.alert import Alert
# Wait for alert
wait = WebDriverWait(driver, 5)
wait.until(EC.alert_is_present())
# Get alert
alert = Alert(driver)
# or: alert = driver.switch_to.alert
# Get alert text
alert_text = alert.text
# Accept (OK)
alert.accept()
# Dismiss (Cancel)
alert.dismiss()
# Send text to prompt
alert.send_keys("Input text")
alert.accept()Handling Frames
# Switch to frame by name or ID
driver.switch_to.frame("frameName")
# Switch to frame by index
driver.switch_to.frame(0)
# Switch to frame by WebElement
frame = driver.find_element(By.CSS_SELECTOR, "iframe.main")
driver.switch_to.frame(frame)
# Switch back to main content
driver.switch_to.default_content()
# Switch to parent frame
driver.switch_to.parent_frame()Handling Multiple Windows
# Get current window handle
main_window = driver.current_window_handle
# Click link that opens new window
driver.find_element(By.LINK_TEXT, "Open New Window").click()
# Get all window handles
handles = driver.window_handles
# Switch to new window
for handle in handles:
if handle != main_window:
driver.switch_to.window(handle)
break
# Do something in new window
print(f"New window title: {driver.title}")
# Close new window and return to main
driver.close()
driver.switch_to.window(main_window)JavaScript Execution
# Scroll to element
element = driver.find_element(By.ID, "footer")
driver.execute_script("arguments[0].scrollIntoView(true);", element)
# Scroll by pixels
driver.execute_script("window.scrollBy(0, 500)")
# Click via JavaScript (bypasses overlays)
driver.execute_script("arguments[0].click();", element)
# Get computed style
color = driver.execute_script(
"return window.getComputedStyle(arguments[0]).color;", element
)
# Wait for page load
driver.execute_script("return document.readyState") == "complete"Taking Screenshots
# Save screenshot to file
driver.save_screenshot("screenshot.png")
# Get screenshot as base64
base64_image = driver.get_screenshot_as_base64()
# Get screenshot as PNG bytes
png_bytes = driver.get_screenshot_as_png()
# Screenshot of specific element
element = driver.find_element(By.ID, "chart")
element.screenshot("element_screenshot.png")Action Chains
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
# Hover over element
element = driver.find_element(By.ID, "menu")
actions.move_to_element(element).perform()
# Double click
actions.double_click(element).perform()
# Right click (context menu)
actions.context_click(element).perform()
# Drag and drop
source = driver.find_element(By.ID, "source")
target = driver.find_element(By.ID, "target")
actions.drag_and_drop(source, target).perform()
# Chain multiple actions
actions.move_to_element(menu)\
.click()\
.move_to_element(submenu)\
.click()\
.perform()Best Practices
1. Always Use Context Managers or Try/Finally
# Option 1: Try/finally
driver = webdriver.Chrome()
try:
# Test code
finally:
driver.quit()
# Option 2: pytest fixtures (recommended)
@pytest.fixture
def driver():
driver = webdriver.Chrome()
yield driver
driver.quit()2. Use Explicit Waits
# Avoid
import time
time.sleep(5) # Bad!
# Prefer
wait = WebDriverWait(driver, 10)
wait.until(EC.element_to_be_clickable((By.ID, "submit")))3. Keep Locators in Page Objects
# Bad: Locators scattered in tests
def test_login():
driver.find_element(By.ID, "username").send_keys("user")
# Good: Locators in page object
class LoginPage:
USERNAME = (By.ID, "username")
def enter_username(self, username):
self.find_element(self.USERNAME).send_keys(username)4. Use Meaningful Assert Messages
# Bad
assert element.is_displayed()
# Good
assert element.is_displayed(), "Login button should be visible on the page"5. Create Reusable Fixtures
# conftest.py
@pytest.fixture
def authenticated_driver(driver, base_url):
"""Return driver with logged-in user."""
login_page = LoginPage(driver)
login_page.open(base_url).login("testuser", "password")
return driver6. Use Markers for Test Organization
@pytest.mark.smoke
def test_homepage_loads():
pass
@pytest.mark.regression
def test_full_checkout_flow():
pass
# Run specific markers
# pytest -m smokeTest Your Knowledge
Quiz on Selenium with Python
Your Score: 0/10
Question: What is the correct way to install Selenium for Python?
Continue Your Selenium Journey
Frequently Asked Questions
Frequently Asked Questions (FAQs) / People Also Ask (PAA)
Should I use pytest or unittest for Selenium tests in Python?
How do I run Selenium tests in headless mode with Python?
What is webdriver-manager and why should I use it?
Why do I get NoSuchElementException even though the element is on the page?
How do I share fixtures across multiple test files?
What is the Page Object Model and why use it with Python Selenium?
How do I take screenshots on test failure in pytest?
How do I handle StaleElementReferenceException in Python Selenium?