去年這個時候,我們還在被 Selenium 的“玄學失敗”折磨得睡不好覺。
每週一晨會,大家聊得最多的不是業務需求,而是:“昨天那幾個腳本又隨機掛了,誰幫忙看看?”
直到一個週五深夜,我第 N 次調試那個經典的 “Element not interactable” 錯誤——明明元素就在頁面上,就是點不了。那一刻我意識到:不是我們寫得不夠好,而是工具已經跟不上時代了。
三個月後,我們完成了核心迴歸套件從 Selenium 到 Playwright 的遷移。結果遠超預期:
- 迴歸時間縮短 60%
- 腳本維護成本下降 70%
- 最重要的是——測試同學終於能在週五準點下班了。 今天想和大家聊聊這段遷移經歷,不吹不黑,只講實話。
一、為什麼非換不可?Selenium 的“中年危機”
Selenium 曾是 Web 自動化的代名詞,這點沒人否認。但今天的前端早已不是十年前的樣子:SPA、動態加載、微前端、Shadow DOM……而 Selenium 的底層架構,還停留在“請求-響應”的靜態時代。
我們最頭疼的三個問題:
1. 隨機失敗像“薛定諤的貓” 同一個腳本,今天跑通,明天就掛。排查半天,發現只是網絡慢了 200ms。
我們至少 30% 的時間花在“救火”上,而不是設計更有效的測試。
2. API 層級太深,代碼臃腫 想等一個元素出現?得這樣寫:
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "submit-btn")) )
還沒開始測邏輯,光等待就寫了三四行。更別説多層嵌套後的可讀性災難。
3. 執行速度慢,資源消耗大 我們的核心迴歸套件(約 300 個用例)在 Selenium 上要跑 2.5 小時。每次上線前,必須提前半天啓動測試,嚴重影響交付節奏。
二、Playwright 快在哪?關鍵在架構
很多人問:“都是控制瀏覽器,差別真有那麼大?”
答案藏在通信機制裏。
Selenium:三層轉發,層層損耗
你的代碼 → WebDriver 協議 → 瀏覽器驅動 → 瀏覽器
每一步都有序列化、反序列化、網絡延遲。就像寄快遞,中間經手人越多,越容易丟件、延誤。
Playwright:直連 DevTools Protocol
你的代碼 → 瀏覽器(通過 CDP)
沒有中間代理,直接與瀏覽器內核對話。這不僅是“快一點”,而是質的飛躍。
我們實測了一個典型場景:打開電商首頁 → 搜索“手機” → 加入購物車,重複 100 次:
三、遷移實戰:如何平穩過渡?
我們沒搞“一刀切”,而是分階段推進。
階段一:雙軌並行,驗證價值(第1個月)新需求:全部用 Playwright 編寫
舊腳本:優先遷移高頻失敗的核心模塊(如登錄、支付) 對比驗證:同一功能兩套實現,看穩定性、速度、維護成本 以“用户登錄”為例:
Selenium 版本(45 行):
def login(username, password):
driver.get(LOGIN_URL)
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "username")))
driver.find_element(By.ID, "username").clear()
driver.find_element(By.ID, "username").send_keys(username) # ... 類似重複操作 ...
WebDriverWait(driver, 10).until(EC.url_contains("/dashboard"))
Playwright 版本(15 行):
async def login(page, username, password):
await page.goto(LOGIN_URL)
await page.fill("#username", username)
await page.fill("#password", password)
await page.click("#login-btn")
await page.wait_for_url("**/dashboard")
代碼量減少 67%,且從未再出現“元素不可交互”類問題。
階段二:釋放 Playwright 的“超能力”(第2–3個月)遷移完成後,我們開始做以前“想做但做不到”的事:
✅ 智能等待,告別 time.sleep()
# 等待元素可見
await page.locator(".product-card").first.wait_for(state="visible")
# 等待特定接口返回
async with page.expect_response("**/api/products") as response_info:
await page.click("#load-more")
✅ 多瀏覽器 + 移動端一鍵切換
# 同一測試,輕鬆跑在 Chromium / Firefox / WebKit
# 甚至模擬 iPhone 14 Pro context = await browser.new_context(device="iPhone 14 Pro")
✅ 內置網絡攔截,Mock 不再依賴第三方
# 支付流程無需真實調用
await page.route("**/api/payment/**", lambda route: route.fulfill( status=200,
body='{"status":"success"}' ))
這些能力,在 Selenium 裏要麼做不到,要麼需要額外集成大量工具鏈。
四、踩過的坑:避雷指南
坑1:選擇器太脆弱
初期我們用錄製工具生成的選擇器:
# ❌ 容易因 DOM 結構變動而失效 "div.main > div:nth-child(3) > button"
後來我們和前端約定:關鍵交互元素加 data-testid 屬性。
# ✅ 穩定、語義清晰 "[data-testid='checkout-button']"
坑2:並行執行爆內存
第一次嘗試併發 50 個測試,機器直接 OOM。
解決方案:用 asyncio.Semaphore 控制併發數。
semaphore = asyncio.Semaphore(10) # 最多10個併發
坑3:動態內容加載判斷不準
對無限滾動頁面,我們改用“元素數量不再增長”或“監聽特定 API 響應”來判斷加載完成,而非硬編碼等待。
五、真實數據對比
我們對商品搜索流程做了完整壓測(搜索 → 翻頁5次 → 點擊加載更多):
更驚喜的是 Trace Viewer 功能——測試失敗後,可以直接回放整個操作過程,像看視頻一樣定位問題。很多偶現 Bug,靠它一錘定音。
六、如果你也想遷移:我的建議路線圖
- 第1周:裝 Playwright,用 playwright codegen 錄一個簡單腳本,感受差異
- 第1月:搭建基礎框架,遷移最不穩定的 10% 用例
- 第2–3月:新需求全用 Playwright,逐步替換舊腳本
- 長期:結合 Git 變更智能調度、視覺迴歸、性能基線等深度優化
七、寫在最後:工具升級,更是思維升級
遷移完成後,團隊裏一位幹了8年的老測試説:
“以前我是 Selenium 的‘消防員’,天天救火;現在我是 Playwright 的‘建築師’,能真正設計質量體系。”
這句話讓我感觸很深。
從 Selenium 到 Playwright,表面是換了個工具,實質是:
從 被動等待 到 智能同步 從 單點執行 到 並行協作 從 功能覆蓋 到 體驗保障 從 腳本維護者 到 質量賦能者 當然,Playwright 也不是萬能藥。如果你還在維護 IE 項目,或者重度依賴 Selenium Grid 的分佈式架構,可能還需謹慎評估。
但對於絕大多數現代 Web 應用——尤其是 React/Vue/Angular 架構——Playwright 帶來的效率提升,是革命性的。
最近面試時,我會問候選人:“如果讓你重新設計自動化框架,你會怎麼做?”
那些能清晰説出 Playwright 優勢,並展示實際落地思考的人,總能讓我眼前一亮。
工具不會決定你的上限,但選對工具,能讓你的價值被看見。
而這,或許才是技術人最該追求的事。