Selenium Python Tutorial with pytest
A Selenium Python tutorial built on pytest: setup with Selenium Manager, locators, explicit waits, fixtures, screenshots on failure, page objects, parallel runs with pytest-xdist and a cloud grid.
What you will learn
- Selenium Python Tutorial with pytest
- Set up Selenium with Python and pytest
- Selenium locators in Python: the By API
- Selenium waits in Python: WebDriverWait and time.sleep
- pytest fixtures for Selenium: conftest.py and scope
- Assertions, screenshots on failure and test reports
- Page objects in Python
- Run Selenium tests in parallel with pytest-xdist
- Run Selenium Python tests on the TestingBot grid
- Selenium Python Quiz
On this page
Last reviewed November 2026 against Selenium 4.52.0, pytest 9.2.0, pytest-xdist 3.8.0, pytest-html 4.2.0 and Python 3.13.
This course builds a small but complete browser test suite in Python. Every example in it was run, first against local Chrome and Firefox and then, in the last chapter, on the TestingBot grid. By the end you will have one project that you can run on your laptop, in CI and on a cloud grid without changing a single test.
Selenium drives the browser, pytest runs the tests#
The suite is built from two libraries that do very different jobs, and most early confusion comes from mixing them up.
Selenium WebDriver is a browser automation library. It starts a browser, navigates, finds elements, clicks, types and reads text back. It has no idea what a test is: it does not know what "passed" means and it will leave a browser running forever if you forget to call quit().
pytest is a test runner. It finds functions whose names start with test_, runs setup and teardown code around them through fixtures, feeds them data, rewrites plain assert statements so failures explain themselves, and reports what passed and failed. It knows nothing about browsers.
| Concern | Who handles it |
|---|---|
| Start and stop a browser | Selenium (webdriver.Chrome(), driver.quit()), called from a pytest fixture |
| Find elements, wait for them, interact | Selenium (By, WebDriverWait, expected_conditions) |
| Discover and select tests | pytest (test_ functions, -k, markers) |
| Check results | pytest (plain assert) |
| Run one test with several inputs | pytest (@pytest.mark.parametrize) |
| Run tests in parallel | pytest-xdist schedules the workers; each test still needs its own browser |
| Screenshots and reports on failure | pytest hooks and plugins, using Selenium to take the screenshot |
Here is the whole idea in a dozen lines:
import pytest
from selenium import webdriver
@pytest.fixture
def driver():
driver = webdriver.Chrome() # Selenium: start a browser
yield driver # pytest: hand it to the test
driver.quit() # pytest runs this after the test, pass or fail
def test_title(driver): # pytest: this is a test
driver.get("https://www.selenium.dev/selenium/web/web-form.html")
assert driver.title == "Web form"
Nothing here downloads a ChromeDriver binary. Since Selenium 4.6, Selenium Manager ships inside the selenium package and finds or downloads the right driver the first time you start a browser. Tutorials that begin with "download chromedriver and add it to your PATH" are out of date.
Why pytest and not unittest?#
Python ships with unittest, and Selenium works with it. pytest has become the usual choice for browser suites because:
-
Fixtures replace
setUp/tearDownclasses. A browser fixture is written once inconftest.pyand any test that namesdriveras an argument gets one. -
Plain
assertgives readable failure diffs withoutassertEqualand friends. - Parametrisation turns one test into many with a decorator.
-
Plugins add parallel runs (pytest-xdist), HTML reports (pytest-html) and much more, and pytest also runs existing
unittesttests unchanged.
If your team already uses unittest, TestingBot has a PyUnit example. This course uses pytest throughout.
What you'll build#
| Chapter | You add |
|---|---|
| 1. Set up Python, Selenium and pytest | a virtual environment, Selenium Manager, a first passing test |
| 2. Locators | the By strategies and a rule for picking stable ones |
| 3. Waits |
WebDriverWait and expected conditions instead of time.sleep
|
| 4. Fixtures | a driver fixture in conftest.py, command-line options, parametrisation |
| 5. Assertions, screenshots and reports | a screenshot for every failure, HTML and JUnit XML reports |
| 6. Page objects | a small page object, and where to go for more |
| 7. Parallel runs | pytest-xdist and the rules that keep parallel tests independent |
| 8. The TestingBot grid | remote browsers, tb:options, pass/fail status, GitHub Actions |
| 9. Quiz | ten questions on the decisions the course made |
The final project looks like this:
selenium-python-course/
conftest.py driver fixture, command-line options, screenshot hook
pytest.ini test paths and markers
requirements.txt pinned versions
pages/
web_form.py page object
tests/
test_web_form.py
test_dynamic.py
The tests use the practice pages the Selenium project publishes for exactly this purpose: a web form and a page that adds elements after a delay. They are public, stable and safe to hit from a test.
Prerequisites#
- Python 3.10 or newer. Selenium 4.52 and pytest 9 both require it. The examples were run on Python 3.13.
- Basic Python: functions, imports, a class or two.
- Chrome installed locally (Firefox too, if you want to follow the cross-browser parts).
- No prior Selenium experience.
If you write Java rather than Python, the sibling course Selenium with Java and TestNG builds the same suite with Maven and TestNG. And for a common misconception up front: there is no official certification from the Selenium project. What employers look for is a suite you can explain, which is what this course builds.