Chapter 02Lesson 05~220 minutes

Checkpoint Lab — Selenium Ecosystem, Installation, and First WebDriver Session

Build a disposable Selenium project from scratch, prove exactly what ran, repeat the minimal session, break driver discovery safely, recover, and leave behind a compact evidence packet that another engineer can interpret.

CheckpointProvenanceTwo sessionsFailure injectionRecovery

Learning objectives

  • Create a Chapter 02 project from an empty directory using an isolated Python virtual environment.
  • Pin Selenium 4.47.0 and verify Python 3.10+ before browser startup.
  • Run the same loopback browser session twice and prove fresh session IDs with stable environment provenance.
  • Inject a reversible missing-driver Service override and classify the resulting failure at the correct layer.
  • Restore the default Selenium Manager path and prove a clean recovery run.
  • Produce a sanitized evidence packet and cleanup/rollback record suitable for a CI installation runbook.

1. Checkpoint acceptance contract

The checkpoint is complete only when you can show all of the following:

  1. an isolated environment with Selenium exactly 4.47.0;
  2. a loopback-only fixture bound to 127.0.0.1:8008;
  3. two successful runs with different session IDs;
  4. recorded browser, browser version, platform, and reported driver version;
  5. one deterministic failure caused by an explicit nonexistent driver path;
  6. a written diagnosis explaining why the failure occurs before session creation;
  7. a restored default Manager run that passes;
  8. screenshots and JSON/text evidence containing no secrets, PII, or personal browser profile;
  9. clean teardown and removal of the disposable project when finished.

2. Setup from an empty directory

PowerShell

The following example makes the Setup from an empty directory behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

$LAB = Join-Path $PWD "selenium-ch02-checkpoint"
Remove-Item -Recurse -Force $LAB -ErrorAction SilentlyContinue
New-Item -ItemType Directory $LAB | Out-Null
Set-Location $LAB
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; assert sys.version_info >= (3,10); assert selenium.__version__ == '4.47.0'; print(sys.version); print(selenium.__version__)" | Set-Content evidence-python-selenium.txt

POSIX shell

The following example makes the Setup from an empty directory behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

LAB="$PWD/selenium-ch02-checkpoint"
rm -rf "$LAB"
mkdir -p "$LAB" && cd "$LAB"
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; assert sys.version_info >= (3,10); assert selenium.__version__ == '4.47.0'; print(sys.version); print(selenium.__version__)" | tee evidence-python-selenium.txt

If your machine already has a reviewed environment you cannot delete, choose a new lab directory rather than reusing it. The checkpoint should never change a personal browser profile or system driver installation.

3. Create the fixture and runner

Create fixture/index.html:

The following example makes the Create the fixture and runner 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 fixture and runner 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()

Create run_session.py:

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

import json
import sys
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/"
run_name = sys.argv[1] if len(sys.argv) > 1 else "run"
out = Path("evidence") / run_name
out.mkdir(parents=True, exist_ok=True)

if urlparse(URL).hostname not in {"127.0.0.1", "localhost", "::1"}:
    raise RuntimeError("checkpoint refuses non-loopback target")

driver = None
try:
    driver = webdriver.Chrome()
    caps = driver.capabilities
    chrome = caps.get("chrome", {})
    public = {
        "selenium": selenium_version,
        "session_id": driver.session_id,
        "browserName": caps.get("browserName"),
        "browserVersion": caps.get("browserVersion"),
        "platformName": caps.get("platformName"),
        "driverVersion": chrome.get("chromedriverVersion", "not-reported"),
    }
    print(json.dumps(public, indent=2))
    (out / "session.json").write_text(json.dumps(public, indent=2), encoding="utf-8")

    driver.get(URL)
    assert driver.title == "Selenium Chapter 02 Fixture"
    assert driver.find_element(By.ID, "result").text == "not confirmed"
    driver.find_element(By.ID, "confirm").click()
    assert driver.find_element(By.ID, "result").text == "confirmed"
    driver.save_screenshot(str(out / "final.png"))
finally:
    if driver is not None:
        driver.quit()

Create break_driver.py:

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

from pathlib import Path
from selenium import webdriver
from selenium.common.exceptions import WebDriverException

missing = Path("disposable-missing-driver") / "chromedriver"
service = webdriver.ChromeService(executable_path=str(missing))
try:
    webdriver.Chrome(service=service)
except WebDriverException as exc:
    Path("evidence").mkdir(exist_ok=True)
    summary = f"{type(exc).__name__}: {str(exc).splitlines()[0]}\n"
    Path("evidence/broken-driver.txt").write_text(summary, encoding="utf-8")
    print(summary, end="")

The broken script does not remove or rename any real executable. It only injects a path that cannot exist inside the disposable directory.

4. Predict before execution

Write evidence/predictions.txt before running:

Prediction 1 — successful run
A fresh WebDriver session ID will be created; the fixture DOM will change from "not confirmed" to "confirmed"; one screenshot and one session JSON file will be written; the session will be closed at process exit.

Prediction 2 — second successful run
The second session ID will differ from the first; binding/browser/driver identity should remain stable unless the environment changed; the fixture begins again at "not confirmed".

Prediction 3 — broken explicit driver path
Session creation will fail before a session ID exists because the configured local driver executable does not exist. No AUT command should run.

Prediction 4 — recovery
Removing the explicit Service override and returning to webdriver.Chrome() will restore the default Selenium Manager path and the loopback session will pass again.

5. Run two clean sessions

Start the fixture server in a separate terminal:

python fixture_server.py

Then run:

The following example makes the Run two clean sessions behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

python run_session.py run-1
python run_session.py run-2

Compare evidence/run-1/session.json and evidence/run-2/session.json. Session IDs must differ. Browser/driver versions should be recorded for both. If they unexpectedly differ, preserve that fact and investigate environment/browser updates instead of editing the evidence.

6. Inject and diagnose the reversible failure

The following example makes the Inject and diagnose the reversible failure behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.

python break_driver.py

Expected evidence is an exception summary indicating that the configured driver path is invalid/unavailable. Exact wording may vary by binding/platform. Your diagnosis must state:

  • the failure occurs during local driver-service setup;
  • no valid WebDriver session ID exists;
  • the loopback fixture was not the cause because navigation never began;
  • the deliberate explicit Service path bypassed the normal Manager-selected driver path;
  • the recovery is to remove that override, not to copy/delete arbitrary system drivers.

7. Optional Manager diagnostic comparison

If the default path itself fails, rerun one small probe with current Manager debug and PATH-skip controls. Keep raw logs private until sanitized.

POSIX

The following example makes the Optional Manager diagnostic comparison 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_SKIP_DRIVER_IN_PATH=true SE_AVOID_STATS=true python run_session.py manager-probe 2> evidence/manager-debug.log || true

PowerShell

The following example makes the Optional Manager diagnostic comparison 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_SKIP_DRIVER_IN_PATH="true"
$env:SE_AVOID_STATS="true"
python run_session.py manager-probe 2> evidence\manager-debug.log
Remove-Item Env:SE_DEBUG,Env:SE_SKIP_DRIVER_IN_PATH,Env:SE_AVOID_STATS

Do not upload the raw log until paths, usernames, proxy endpoints/credentials, and other environment-specific details are reviewed.

8. Prove recovery

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

python run_session.py recovered

A successful recovery must produce a new session ID and fresh evidence/recovered/session.json plus screenshot. Do not claim recovery merely because the browser window appears; the assertions and evidence files must complete and teardown must run.

9. Verification checklist

  • evidence-python-selenium.txt proves Python 3.10+ and Selenium 4.47.0.
  • Run 1 and Run 2 have different session IDs.
  • Both successful JSON files record browser name/version, platform, and reported driver version.
  • Both screenshots show only the synthetic loopback fixture.
  • broken-driver.txt records the injected Service-path failure.
  • The diagnosis correctly states that no WebDriver session existed for the broken run.
  • The recovered run passes with a fresh session ID.
  • No production URL, credential, personal profile, Authorization header, proxy password, or PII appears in evidence.
  • No browser is intentionally left open after the scripts exit.

10. Cleanup and rollback

Stop the fixture server with Ctrl+C. Preserve only the evidence you actually need, then remove the disposable directory.

PowerShell

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

deactivate
Set-Location ..
Remove-Item -Recurse -Force selenium-ch02-checkpoint

POSIX

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

deactivate
cd ..
rm -rf selenium-ch02-checkpoint

No system browser, PATH entry, Selenium Manager cache, corporate proxy, or global Python installation should have been modified by the checkpoint. If your environment downloaded a Manager-managed driver/browser asset into its normal cache, that cache is shared environment state; do not delete it automatically as “cleanup.”

11. What Chapter 02 adds to the operating model

You now have an installation contract: project dependency identity, browser source, driver-management ownership, session provenance, safe target boundary, guaranteed teardown, and a reproducible startup diagnostic path. Chapter 03 deepens the protocol boundary by examining W3C WebDriver architecture, session negotiation, capabilities, and the command/response model that the Chapter 02 constructor hides behind one line.

Knowledge check

What proves the two successful runs were fresh sessions?

Why is the broken-driver test safer than renaming a real chromedriver?

What observation proves the broken run failed before AUT interaction?

Why should the Manager cache not be blindly deleted during cleanup?

What is the Chapter 03 bridge?

Next chapter

WebDriver architecture and protocol

Chapter 03 opens the session constructor and explains how capabilities are negotiated, how commands cross the W3C WebDriver boundary, and how local and remote execution share the same protocol model.

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.