IT Training
Blog
Search

Selenium Tutorial for Beginners: Automate Your First Web Test With Python

Developer workstation with code on a monitor and a laptop

Selenium is a free, open-source tool that automates web browsers, and it is the most widely used starting point for web test automation. In this Selenium tutorial for beginners you will set up Selenium with Python, learn how to find elements, write your first login test, handle waits correctly, and organise tests with pytest and the Page Object Model. Every code sample runs against a public practice website, so you can follow along today.

What is Selenium?

Selenium is a family of tools. The part you will use is Selenium WebDriver, a library that controls a real browser (Chrome, Firefox, Edge or Safari) through code. You write a script that opens pages, clicks buttons, types text and checks the results, exactly as a manual tester would, but repeatably and much faster.

WebDriver supports Java, Python, C#, JavaScript and Ruby. Java is common in Indian service companies, while Python is quicker to learn. The concepts are identical, so this tutorial uses Python for shorter code.

Step 1: Set up your environment

python -m venv venv
# Windows: venv/Scripts/activate   macOS or Linux: source venv/bin/activate
pip install selenium pytest

You also need Google Chrome or another browser installed. Since Selenium 4.6, a built-in tool called Selenium Manager downloads the matching browser driver automatically, so you no longer need to download ChromeDriver by hand.

Step 2: Your first script

from selenium import webdriver

driver = webdriver.Chrome()
driver.get('https://the-internet.herokuapp.com/')
print(driver.title)
driver.quit()

Run it with python first.py. A Chrome window opens, loads the page, prints the title and closes. Always call driver.quit(), or browser processes will pile up in the background.

Step 3: Find elements with locators

To interact with a page, you first locate elements. Selenium offers several strategies through the By class:

LocatorExampleWhen to use
By.ID(By.ID, 'username')First choice when a stable id exists
By.NAME(By.NAME, 'email')Form fields
By.CSS_SELECTOR(By.CSS_SELECTOR, 'button.radius')Fast and flexible; preferred for most cases
By.XPATH(By.XPATH, '//h2[text()="Login Page"]')Matching by text or complex relationships
By.LINK_TEXT(By.LINK_TEXT, 'Form Authentication')Links with exact visible text

Use your browser’s developer tools (right-click, then Inspect) to see an element’s attributes. Prefer short, meaningful locators. Long absolute XPaths such as /html/body/div[2]/div/form/input[1] break whenever the layout changes. If you work with developers, ask them to add data-testid attributes for automation.

Step 4: Write a login test

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get('https://the-internet.herokuapp.com/login')

driver.find_element(By.ID, 'username').send_keys('tomsmith')
driver.find_element(By.ID, 'password').send_keys('SuperSecretPassword!')
driver.find_element(By.CSS_SELECTOR, 'button[type=submit]').click()

message = driver.find_element(By.ID, 'flash').text
assert 'You logged into a secure area!' in message
print('Login test passed')
driver.quit()

The practice site publishes these demo credentials on its login page. Notice the pattern used in every UI test: arrange (open the page), act (type and click), assert (check the result).

Step 5: Use waits the right way

Modern web pages load content dynamically. If your script looks for an element before it appears, you get a NoSuchElementException. The fix is to wait for a condition, not to add time.sleep() everywhere.

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 10)
flash = wait.until(EC.visibility_of_element_located((By.ID, 'flash')))
  • Explicit waits (above) wait up to a maximum time for a specific condition, then continue as soon as it is true. Use these.
  • Implicit waits set a global timeout for every element lookup. Avoid mixing them with explicit waits, as the combination causes unpredictable delays.
  • Fixed sleeps make tests slow and still flaky. Use them only while debugging.

Step 6: Organise tests with pytest

Scripts are fine for learning, but real projects use a test framework. With pytest, any function whose name starts with test_ is a test, and fixtures handle setup and cleanup.

# conftest.py
import pytest
from selenium import webdriver

@pytest.fixture
def driver():
    d = webdriver.Chrome()
    d.implicitly_wait(0)
    yield d
    d.quit()
# test_login.py
from selenium.webdriver.common.by import By

def test_valid_login(driver):
    driver.get('https://the-internet.herokuapp.com/login')
    driver.find_element(By.ID, 'username').send_keys('tomsmith')
    driver.find_element(By.ID, 'password').send_keys('SuperSecretPassword!')
    driver.find_element(By.CSS_SELECTOR, 'button[type=submit]').click()
    assert 'secure area' in driver.find_element(By.ID, 'flash').text

def test_invalid_password(driver):
    driver.get('https://the-internet.herokuapp.com/login')
    driver.find_element(By.ID, 'username').send_keys('tomsmith')
    driver.find_element(By.ID, 'password').send_keys('wrong')
    driver.find_element(By.CSS_SELECTOR, 'button[type=submit]').click()
    assert 'invalid' in driver.find_element(By.ID, 'flash').text.lower()

Run all tests with pytest -v. Notice the negative test: good testers check what should fail, not only what should pass.

Step 7: The Page Object Model

When many tests use the same page, keep that page’s locators and actions in one class. If the page changes, you update one file instead of fifty tests.

# pages/login_page.py
from selenium.webdriver.common.by import By

class LoginPage:
    URL = 'https://the-internet.herokuapp.com/login'
    USER = (By.ID, 'username')
    PASS = (By.ID, 'password')
    SUBMIT = (By.CSS_SELECTOR, 'button[type=submit]')
    FLASH = (By.ID, 'flash')

    def __init__(self, driver):
        self.driver = driver

    def open(self):
        self.driver.get(self.URL)

    def login(self, user, password):
        self.driver.find_element(*self.USER).send_keys(user)
        self.driver.find_element(*self.PASS).send_keys(password)
        self.driver.find_element(*self.SUBMIT).click()

    def message(self):
        return self.driver.find_element(*self.FLASH).text

Your test then reads like plain English: open the login page, log in, check the message.

Other things you will need

  • Dropdowns: the Select class for standard select elements.
  • Alerts, frames and new tabs: driver.switch_to.alert, switch_to.frame and switch_to.window.
  • Screenshots on failure: driver.save_screenshot('fail.png'), ideally triggered from a pytest hook.
  • Headless runs for CI servers, using browser options.
  • Reports with a plugin such as pytest-html or Allure.
  • Running in CI with Jenkins or GitHub Actions, so tests run on every code change.

What should you automate?

Automate stable, repetitive, high-value checks: login, checkout, search, and regression suites that run on every release. Keep exploratory testing, usability checks and one-off scenarios manual. Our guide to manual vs automation testing explains the trade-offs.

Frequently asked questions

Should I learn Selenium with Java or Python?

Check the job listings you are targeting. Java with TestNG is very common in service companies; Python with pytest is quicker to learn. The WebDriver concepts transfer directly between them.

Do I need manual testing knowledge before Selenium?

Yes. Automation only speeds up testing. You still need to know what to test, how to write test cases and how to report bugs.

Is Selenium still relevant with Playwright and Cypress available?

Yes. Newer tools are popular for new projects, but Selenium has a huge installed base, supports many languages and browsers, and is still listed in a large number of testing jobs. Learning it also makes other tools easy to pick up.

Why do my Selenium tests fail randomly?

The usual causes are missing waits, fragile locators, tests that depend on each other, and shared test data. Fix waits and locators first.

Prepare for interviews with our manual testing interview questions, and explore more software testing tutorials.

Other articles

Radium4 — IT training blog

Practical, plain-English guides for DevOps, cloud, data, Python, testing and security learners.