Chapter 1 · E2E Testing and Playwright Overview
Chapter goals: Understand where E2E testing sits in the test pyramid, clarify Playwright's architectural advantages over Selenium, install Playwright, and get the environment running.
1.1 E2E in the Test Pyramid
The classic test pyramid is divided into three layers from bottom to top:
| Layer | Quantity | Speed | Stability | What It Verifies |
|---|---|---|---|---|
| Unit Tests | Most | Milliseconds | Very stable | Function/class logic |
| Integration Tests | Moderate | Seconds | Fairly stable | Module collaboration, databases, APIs |
| E2E Tests | Fewest | Tens of seconds | Fragile | Complete user journeys in a real browser |
E2E (End-to-End) testing simulates the full process of "a real user opens a browser → interacts with the page → sees the result." It's slow and expensive, but it's the only thing that answers one question: does it actually work when users use it? So the principle for production practice is "few but precise" — only cover critical paths (login, checkout, payment), not every branch.
Positioning of this course
The pytest course on this site covers the lower half of the pyramid; this course focuses on the top layer. Both courses share the same pytest knowledge system — from Chapter 6 onward you'll reuse fixture skills extensively.
1.2 Selenium WebDriver vs Playwright: Architecture Determines Experience
The older Selenium WebDriver approach works by "external remote control":
test code → HTTP request → WebDriver driver process → browser extension injects JS → pageEvery operation requires cross-process communication, and the driver has no idea when the page has finished rendering — you can only patch it with manual time.sleep(3) or explicit waits. This is the root cause of flaky tests in the Selenium era.
Playwright does things completely differently: the test process talks to the browser through a direct protocol pipe (Chromium uses CDP, Firefox/WebKit use patched kernel-inbuilt protocols):
test code ←persistent connection→ browser kernel
(event-driven, bidirectional push)This brings three qualitative improvements:
- Fast: no intermediate driver layer; operations go directly to the kernel;
- Auto-wait: the kernel knows the page's real state; Playwright auto-blocks until an element is "visible, stable, and actionable";
- Event reliability:
clickfires a real input event, not a synthesized JS event; you can also listen to network, console, and other kernel-level signals.
1.3 Three Browser Engines
Playwright supports all modern rendering engines:
- Chromium — the engine behind Chrome/Edge, the daily default;
- Firefox — a patched version precisely driven by the automation protocol;
- WebKit — Safari's engine, letting you test Safari behavior on Linux/Windows.
One API across three engines means you can cover Safari compatibility from a Linux CI without needing a Mac farm.
1.4 Sync and Async Dual APIs
Playwright Python provides both sync_api and async_api mirror APIs:
# Sync style: intuitive, great for scripts and most tests
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com")
print(page.title())
browser.close()# Async style: ideal for asyncio apps and high-concurrency scraping/load-testing
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto("https://example.com")
print(await page.title())
await browser.close()
asyncio.run(main())Both APIs are fully equivalent; the async version just adds await to every method. The first half of this course focuses on sync; Chapter 18 covers async in depth.
1.5 Installation and Environment Verification
# Install the library
pip install playwright
# Download all three engines (one-time, ~hundreds of MB)
playwright install
# Optional: install system deps (often needed on Linux)
playwright install-depsVerify the install:
playwright --version
# Version 1.62.xThen run a minimal smoke script smoke.py:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://example.com")
assert "Example Domain" in page.title(), "Title mismatch"
print("✅ Environment OK, title:", page.title())
browser.close()python smoke.py
# ✅ Environment OK, title: Example DomainVersion pinning
The playwright pip package and browser engines are tightly coupled: after upgrading the pip package, you must re-run playwright install to download the matching engine versions, or you'll get "executable not found" errors.
1.6 Chapter Summary
- E2E sits at the top of the test pyramid: few but precise, only test critical user paths;
- Playwright connects directly to the browser kernel protocol — faster, auto-waits, more realistic events than Selenium;
- One API covers Chromium / Firefox / WebKit;
- Both
sync_apiandasync_apiare fully equivalent; - Remember to re-run
playwright installafter upgrading the Playwright package.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. What is the most fundamental architectural difference between Playwright and Selenium WebDriver?
2. To verify Safari compatibility on a Linux CI, which engine should you use?
3. After upgrading the playwright pip package you get "browser executable not found". Why?
4. Which statement about sync_api and async_api is correct?
🛠️ Hands-On Exercises
- Write a script using the sync API that visits
example.comandplaywright.devin sequence, printing each<title>. - Rewrite exercise 1 in async style; compare the two approaches.
- Run your script with both
headless=Trueandheadless=False; observe the difference. Addslow_mo=1000to feel the slow-motion effect.
With the environment ready, proceed to Chapter 2: First Script and Sync API.