Python Testing Guide

This guide helps contributors write tests in the Selenium Python codebase.

Test Framework

  • Tests use pytest.
  • Test HTML pages live in common/src/web/.
  • pages fixture loads test pages via pages.load("pageName.html").
  • Assertions use standard pytest assert statements.
import pytest
from selenium.webdriver.common.by import By

def test_element_is_displayed(driver, pages):
    pages.load("javascriptPage.html")

    element = driver.find_element(By.ID, "displayed")
    assert element.is_displayed() is True

@pytest.mark.xfail_safari(reason="Safari doesn't support this")
def test_something_safari_fails(driver, pages):
    # Expected to fail on Safari
    pass

Running Tests

Bazel creates test targets for each browser. Tests run in parallel by default.

bazel test //py/...  # All tests
bazel test //py:unit  # Unit tests (no browser)
bazel test //py:test-chrome  # Chrome browser tests
bazel test //py:test-firefox  # Firefox browser tests
bazel test //py:test-chrome-common  # Common (cross-browser) tests with Chrome

# A single test file with Chrome (target = test/<path>/<file>-<browser>[-variant]).
# Discover exact names with: bazel query //py:all | grep window_tests
bazel test //py:test/selenium/webdriver/common/window_tests-chrome

# With BiDi protocol
bazel test //py:test-chrome-bidi

# Test filters
bazel test //py/... --test_tag_filters=chrome

# Additional arguments
bazel test //py/... --flaky_test_attempts=3
bazel test //py/... --test_output=all
bazel test //py/... --test_output=streamed  # Live output for debugging
bazel test //py:test-chrome --headless

# Run a specific test in a test file
bazel test //py:test/selenium/webdriver/common/bidi_browsing_context_tests-chrome-bidi \
  --test_arg=-k \
  --test_arg=test_get_tree_with_child \

# View all targets
bazel query //py/...

Running Tests Without Bazel (using pytest)

You can run tests directly with pytest after setting up the development environment.

Setup

First, install the required dependencies:

pip install -r py/requirements_lock.txt

Then build the generated files and copy them into your local source tree:

./go py:local_dev

Running with pytest

# Run all tests in a directory
pytest py/test/selenium/webdriver/chrome/ --driver chrome

# Run a specific test file
pytest py/test/selenium/webdriver/common/window_tests.py

# Run a specific test function
pytest py/test/selenium/webdriver/common/window_tests.py::test_should_get_the_size_of_the_current_window

# With pytest options
pytest py/test/selenium/webdriver/chrome/ --driver chrome --headless -v

Note: For running BiDi tests, use the --bidi flag.

Skipping Tests

Skips use pytest markers; each accepts optional reason and run parameters (run=False skips the test entirely instead of expecting a failure).

MarkerWhen to Use
@pytest.mark.xfail_chromeTest expected to fail on Chrome
@pytest.mark.xfail_firefoxTest expected to fail on Firefox
@pytest.mark.xfail_safariTest expected to fail on Safari
@pytest.mark.xfail_edgeTest expected to fail on Edge
@pytest.mark.xfail_ieTest expected to fail on IE
@pytest.mark.xfail_remoteTest expected to fail with Remote WebDriver
@pytest.mark.xfail_chrome(reason="Not implemented yet")
@pytest.mark.xfail_firefox(reason="https://bugzilla.mozilla.org/123")
def test_something(driver, pages):
    pass

@pytest.mark.xfail_safari(run=False)  # Skip entirely instead of xfail
def test_skip_safari(driver, pages):
    pass

Driver Lifecycle

MarkerWhen to Use
@pytest.mark.no_driver_after_testTeardown driver after test
@pytest.mark.needs_fresh_driverRestart driver for test isolation

Fixtures

We make use of pytest fixtures to simplify test setup/teardown. There are several built-in pytest fixtures, and many of our own internal fixtures. If a fixture is specific to a module, you will find it defined within the test file that uses it. If it is shared among several modules, you will find the main fixtures in conftest.py:

FixtureDescription
driverWebDriver instance, auto-parametrized by browser
pagesLoad test pages: pages.load("page.html") or pages.url("page.html")
webserverTest HTTP server reference
clean_driverFresh driver without parametrization
clean_optionsFresh browser options instance

Test Organization

py/test/
├── unit/                    # Unit tests (no browser)
│   └── selenium/webdriver/
└── selenium/webdriver/      # Integration tests
    ├── common/              # Cross-browser tests
    ├── chrome/
    ├── firefox/
    ├── safari/
    └── remote/

Test files end in _tests.py (e.g., visibility_tests.py).

Build Files

  • Adding tests shouldn't require Bazel changes—files matching *_tests.py are picked up automatically.
  • Make sure the test file is in a directory covered by existing py_test_suite targets.