Selenium:自动化界的“老黄牛”
Selenium 诞生于 2004 年,是自动化测试领域的绝对霸主,它的优缺点都源于它的“资历”。
优点:
- **无可比拟的兼容性(最大优势):** Selenium 支持几乎所有的主流浏览器(Chrome、Firefox、Safari、Edge,甚至老旧的 IE)和操作系统。如果你的项目需要测试古老的 IE 浏览器,Selenium 是唯一的选择。
- **语言支持最广:** 它支持 Java、Python、C#、JavaScript、Ruby、PHP 等几乎所有主流编程语言。无论团队的技术栈是什么,都能无缝接入 Selenium。
- **生态成熟,资料丰富:** 经过近 20 年的积累,Selenium 拥有庞大的社区。遇到任何报错,几乎都能在网上搜到解决方案。它与 Appium(移动端自动化)同源,理念互通,便于扩展。
- **企业级支持:** 许多传统大型企业的老项目,其测试框架底层都是基于 Selenium 构建的,相关的 Jenkins、Grid 集群等基础设施非常完善。
缺点:
- **稳定性差(最头疼的问题):** Selenium 在执行时经常因为页面加载慢、网络波动而出现“元素找不到”的错误。为了保证脚本不崩,测试人员必须写大量的
sleep(强制等待)或复杂的“显式等待”代码,这让脚本变得臃肿且难以维护。 - **速度慢:** Selenium 通过 HTTP 协议与浏览器驱动(WebDriver)通信,中间环节多,就像寄信一样一来一回,执行速度相对较慢,尤其是在大规模并发执行时。
- **配置繁琐:** 需要手动管理浏览器驱动版本(虽然现在有 Selenium Manager 改善了,但历史上这是个很大的坑),环境搭建对新手不够友好。
- **调试困难:** 脚本跑失败了,通常只能看到一张截图和一堆报错日志,很难还原当时的页面状态(比如 DOM 结构变化过程),排查问题像“破案”。
Playwright:新时代的“智能刺客”
Playwright 由微软推出,专为现代 Web 应用(React/Vue/Angular)设计,它的优缺点正好针对 Selenium 的痛点。
优点:
- **极高的稳定性(杀手锏):** Playwright 内置了“自动等待”机制。它在点击或输入之前,会自动检查元素是否可见、可交互、且处于稳定状态。这意味着你几乎不需要写等待代码,脚本“脾气”很好,不容易因为网络慢而报错。
- **执行速度快:** 它使用 WebSocket 协议与浏览器内核直接通信,像打电话一样实时传输指令,加上浏览器上下文(Browser Context)复用技术,启动和执行速度远超 Selenium。
- **开箱即用:** 安装时一条命令就能自动下载匹配的浏览器和驱动,无需担心版本兼容问题。自带强大的 CodeGen(录制工具)和 Trace Viewer(追踪工具),调试时可以像看视频一样回放测试过程,精准定位问题。
- **强大的现代特性:** 原生支持拦截网络请求(Mock 数据)、模拟地理位置、权限(如通知)以及移动端视口。它对处理复杂的 iframe、Shadow DOM 和文件下载上传也非常优雅,无需像 Selenium 那样切换上下文。
缺点:
- **不支持老旧环境:** Playwright 不支持 Internet Explorer (IE)。如果你的客户还在用 IE,那就告别 Playwright 吧。
- **无法测试原生移动 App:** Playwright 只能模拟移动端的浏览器环境(H5),不能像 Appium 那样去控制手机上的原生 App(如微信、抖音客户端)。
- **生态相对年轻:** 虽然发展迅猛,但毕竟发布较晚,在遇到一些非常冷门、奇葩的浏览器行为或企业级定制化需求时,可能不如 Selenium 那样有现成的轮子可用。
- **语言支持略少:** 虽然也支持主流语言,但对于 Ruby、PHP 等小众语言的支持力度远不如 Selenium。
总结性的比喻
- Selenium 像一辆手动挡的老吉普车。它能去任何地方(兼容性强),修车铺遍地都是(资料多),但是开起来很累,需要时刻踩离合(写等待代码),容易熄火(脚本报错)。
- Playwright 像一辆顶配的自动挡特斯拉。开起来很爽,基本不用操心换挡(自动等待),有全景影像辅助(Trace Viewer),跑得飞快。但是它不能下深沟烂路(不支持 IE),也不能开出公路去越野(不能测原生 App)。
**目前的行业趋势是:** 新项目、Web 项目、追求效率的团队首选 Playwright;维护老项目、需要测 IE 或原生 App 的团队继续使用 Selenium。
需要我为你梳理一份从 Selenium 迁移到 Playwright的注意事项清单,或者帮你分析如何向团队推荐引入 Playwright吗?
我们使用代码简单看一下两者之间的差距
测试目标:
- 打开登录页
- 输入用户名和密码
- 点击登录
- 验证登录成功后的欢迎信息
- 处理可能出现的“验证码弹窗”(模拟真实世界的干扰)
首先来看selenium:
import timefrom selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.common.exceptions import TimeoutException, NoSuchElementExceptionfrom selenium.webdriver.chrome.service import Servicefrom webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))driver.maximize_window()
driver.get("https://example.com/login")
# 3. 显式等待元素出现(Selenium必须写,否则容易报错)
wait = WebDriverWait(driver, 10)
# 4. 定位元素并操作
username_input = wait.until(EC.visibility_of_element_located((By.ID, "username")))
username_input.send_keys("admin")
password_input = wait.until(EC.visibility_of_element_located((By.ID, "password")))
password_input.send_keys("123456")
# 处理可能的干扰(例如:随机出现的广告或弹窗)
try:
close_btn = driver.find_element(By.CSS_SELECTOR, ".modal-close")
close_btn.click()
print("关闭了干扰弹窗")
except NoSuchElementException:
pass # 没找到就算了
# 5. 点击登录
login_button = wait.until(EC.element_to_be_clickable((By.ID, "loginBtn")))
login_button.click()
# 6. 断言(必须等待结果出现)
welcome_text = wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "welcome")))
assert "Welcome" in welcome_text.text
print("测试通过!")
except TimeoutException:print("测试失败:元素加载超时")driver.save_screenshot("selenium_error.png")
finally:# 7. 清理time.sleep(2) # 强制等待2秒看结果driver.quit()
再来看一下Playwright
pip install playwright
playwright install
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError
with sync_playwright() as p:# 1. 初始化(自动下载和管理驱动)browser = p.chromium.launch(headless=False, slow_mo=1000) # slow_mo便于观察page = browser.new_page()
# 2. 导航
page.goto("https://example.com/login", timeout=60000)
# 3. 操作(Playwright 内置自动等待,无需显式写 wait)
# 它会自动等元素可见、可点击、稳定
page.fill("#username", "admin")
page.fill("#password", "123456")
# 4. 处理干扰(Playwright 的 Locator 机制更稳健)
# locator 是“弹性”的,如果元素不存在,不会立即报错,除非你强制操作
close_btn = page.locator(".modal-close")
if close_btn.is_visible():
close_btn.click()
print("关闭了干扰弹窗")
# 5. 点击登录
page.click("#loginBtn")
# 6. 断言(Playwright 自带的 expect 断言库,包含自动重试逻辑)
# 即使网络慢,它会一直尝试直到超时,而不是立刻失败
expect(page.locator(".welcome")).to_contain_text("Welcome", timeout=10000)
print("测试通过!")
# 7. 清理
browser.close()