Chapter 02Lesson 02~150 minutes

Selenium Ecosystem, Installation, and First WebDriver Session: Guided Hands-On Workflow

Create the first reproducible Selenium project from a clean directory and prove every important state transition: dependency install, local fixture startup, session negotiation, navigation, interaction, evidence capture, and teardown.

Python venvLoopback fixtureSelenium ManagerCapabilitiesTeardown

Learning objectives

  • Create an isolated Python environment and pin Selenium 4.47.0 at project scope.
  • Run a loopback-only HTML fixture so the exercise cannot mutate a public or production application.
  • Use the default Selenium Manager path to resolve a compatible local driver without hard-coded executable paths.
  • Record session ID, browser/driver capabilities, URL, title, assertion state, and one screenshot as evidence.
  • Use try/finally so the session is terminated even when an assertion or command fails.
  • Use Manager debug settings only for diagnosis and sanitize personal paths before sharing logs.

1. Preflight and assumptions

Use Python 3.10 or newer, Selenium 4.47.0, and one supported local Chromium-family browser. The first driver resolution may need network access if the matching driver is not already cached. The application target is http://127.0.0.1:8008/ only.

Do not use a real account or production URL. The fixture has no authentication, no external requests, and no persistent server-side state. If your corporate environment requires an approved proxy for Selenium Manager, configure that through the organization’s supported mechanism; do not disable TLS verification.

2. Create an isolated project environment

Windows PowerShell

The following example makes the Create an isolated project environment behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

New-Item -ItemType Directory selenium-ch02 -Force | Out-Null
Set-Location selenium-ch02
py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install selenium==4.47.0
python -c "import sys, selenium; print(sys.version); print('selenium='+selenium.__version__)"
python -m pip freeze | Set-Content evidence-dependencies.txt

Linux/macOS shell

The following example makes the Create an isolated project environment behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

mkdir -p selenium-ch02 && cd selenium-ch02
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install selenium==4.47.0
python -c "import sys, selenium; print(sys.version); print('selenium='+selenium.__version__)"
python -m pip freeze > evidence-dependencies.txt

Pinning the binding makes the project dependency reproducible. It does not pin the system browser automatically. That is why the session evidence below records the actual browser and driver versions.

3. Create the disposable loopback fixture

Create fixture/index.html:

The following example makes the Create the disposable loopback fixture behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>Selenium Chapter 02 Fixture</title></head>
<body>
  <main>
    <h1 id="status">Local fixture ready</h1>
    <button id="confirm" type="button">Confirm session</button>
    <p id="result" aria-live="polite">not confirmed</p>
  </main>
  <script>
    document.querySelector('#confirm').addEventListener('click', () => {
      document.querySelector('#result').textContent = 'confirmed';
    });
  </script>
</body>
</html>

Create fixture_server.py:

The following example makes the Create the disposable loopback fixture behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler
from functools import partial
from pathlib import Path

ROOT = Path(__file__).parent / "fixture"
server = ThreadingHTTPServer(("127.0.0.1", 8008), partial(SimpleHTTPRequestHandler, directory=str(ROOT)))
print("fixture=http://127.0.0.1:8008/")
try:
    server.serve_forever()
except KeyboardInterrupt:
    pass
finally:
    server.server_close()

Start it in a second terminal from the activated project environment:

python fixture_server.py

Open http://127.0.0.1:8008/ manually once if you want a read-only sanity check. The page should show “Local fixture ready.”

4. Create the first observable session

Create session.py:

The following example makes the Create the first observable session behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

import json
from pathlib import Path
from urllib.parse import urlparse
from selenium import __version__ as selenium_version
from selenium import webdriver
from selenium.webdriver.common.by import By

URL = "http://127.0.0.1:8008/"
EVIDENCE = Path("evidence")
EVIDENCE.mkdir(exist_ok=True)

def require_loopback(url: str) -> None:
    host = urlparse(url).hostname
    if host not in {"127.0.0.1", "localhost", "::1"}:
        raise RuntimeError(f"Refusing non-loopback target: {host}")

def public_caps(caps: dict) -> dict:
    chrome = caps.get("chrome", {})
    return {
        "selenium": selenium_version,
        "browserName": caps.get("browserName"),
        "browserVersion": caps.get("browserVersion"),
        "platformName": caps.get("platformName"),
        "driverVersion": chrome.get("chromedriverVersion", "not-reported"),
    }

require_loopback(URL)
driver = None
try:
    driver = webdriver.Chrome()
    print(f"session_id={driver.session_id}")
    data = public_caps(driver.capabilities)
    print(json.dumps(data, indent=2))
    (EVIDENCE / "capabilities.json").write_text(json.dumps(data, indent=2), encoding="utf-8")

    driver.get(URL)
    print(f"url={driver.current_url}")
    print(f"title={driver.title}")
    assert driver.title == "Selenium Chapter 02 Fixture"
    assert driver.find_element(By.ID, "status").text == "Local fixture ready"
    driver.find_element(By.ID, "confirm").click()
    assert driver.find_element(By.ID, "result").text == "confirmed"
    driver.save_screenshot(str(EVIDENCE / "session.png"))
finally:
    if driver is not None:
        driver.quit()

The sequence matters. The loopback guard proves target scope before any browser command. The WebDriver constructor creates the session. Returned capabilities prove what started. Navigation mutates only the browser context. The click mutates only the fixture DOM. The screenshot writes one local evidence file. The finally block closes the session even if an assertion fails.

5. Run and interpret the first execution

The following example makes the Run and interpret the first execution behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

python session.py

Expected output shape (your versions and session identifier will differ):

session_id=<opaque session id>
{
  "selenium": "4.47.0",
  "browserName": "chrome",
  "browserVersion": "<installed or managed browser version>",
  "platformName": "<platform>",
  "driverVersion": "<reported driver version or not-reported>"
}
url=http://127.0.0.1:8008/
title=Selenium Chapter 02 Fixture

Verify that evidence/capabilities.json contains no credential or profile path, evidence/session.png shows the synthetic fixture, and no browser window remains after the process exits.

6. Inspect Selenium Manager only when you need more evidence

Most successful runs should not require direct Manager interaction. When resolution fails, current Selenium Manager accepts environment-based configuration such as SE_DEBUG=true, SE_SKIP_DRIVER_IN_PATH=true, SE_OFFLINE=true, SE_CACHE_PATH, and SE_PROXY. Use these as narrow diagnostic controls.

POSIX shell diagnostic example

The following example makes the Inspect Selenium Manager only when you need more evidence behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

SE_DEBUG=true SE_AVOID_STATS=true python session.py 2> manager-debug.log || true
# Review locally. Do not publish raw personal paths or proxy credentials.

PowerShell diagnostic example

The following example makes the Inspect Selenium Manager only when you need more evidence behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

$env:SE_DEBUG = "true"
$env:SE_AVOID_STATS = "true"
python session.py 2> manager-debug.log
Remove-Item Env:SE_DEBUG
Remove-Item Env:SE_AVOID_STATS

Debug output can include cache or executable paths. Treat the raw log as local diagnostic evidence; redact usernames, home directories, proxy credentials, and private filesystem locations before attaching it to a ticket.

7. Small challenge: prove a second clean session

Run session.py a second time. Compare the two session IDs—they should differ because each run creates a fresh session. Compare the browser/driver versions—they should normally be stable within the same environment. Confirm the fixture result returns to not confirmed on initial page load because page state is recreated, and verify no prior browser session is reused.

Then add one line that prints driver.capabilities.get("acceptInsecureCerts"). Do not change the capability yet; simply observe the negotiated value. The goal is to practice inspection before configuration.

Knowledge check

Why pin selenium==4.47.0 but still record browserVersion?

What does the loopback guard protect against?

Why capture capabilities before complex interactions?

When should SE_DEBUG be enabled?

What evidence proves clean lifecycle after two runs?

Next lesson

Choose the installation strategy deliberately

Lesson 3 compares Manager versus manual driver control, system versus managed browsers, headed versus headless execution, dependency pinning, and binding choices.

Official references and version notes

Version and compatibility note

Version-sensitive behavior was rechecked against current Selenium primary documentation on 2026-08-27. The mandatory path pins the Python binding to Selenium 4.47.0, requires Python 3.10+, uses one supported local Chromium-family browser for the runnable workflow, relies on Selenium Manager as the default driver-management path, and targets only a loopback fixture. The exact browser and driver versions are intentionally discovered from the created session rather than hard-coded. Selenium Grid, WebDriver BiDi, paid browser clouds, enterprise identity, and hosted CI are not required in Chapter 02.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.