自动化测试面试题¶
自动化测试面试不会只问“会不会 Selenium”。更常见的是问你为什么做自动化、怎么选场景、怎么保证稳定、失败怎么排查。
什么样的用例适合自动化?¶
推荐回答:
自动化测试框架一般包含哪些模块?¶
可以回答:
- 用例层:组织测试用例。
- 请求或页面封装层:封装接口请求或页面操作。
- 数据层:管理测试数据。
- 配置层:环境地址、账号、超时时间。
- 断言层:判断结果是否符合预期。
- 报告层:生成 Allure、HTML 等报告。
- 日志层:记录失败原因。
- CI 集成:在流水线中自动运行。
Selenium 和 Playwright 怎么选?¶
如果公司已有 Selenium 框架,我会优先维护现有框架,因为迁移成本高。
如果是新项目,我更倾向 Playwright,因为它自动等待能力强,调试工具和 trace 对定位问题比较友好。
但最终要看团队技术栈、浏览器要求和维护成本。
Playwright 的自动等待机制是怎么工作的?¶
回答思路:先说原理,再对比 Selenium。
Playwright 在执行操作前会自动等待元素满足 5 个条件:
- 元素存在于 DOM 中
- 元素可见(非
display:none或visibility:hidden) - 元素稳定(不在动画中)
- 元素未被遮挡
- 元素启用(非
disabled)
默认超时 30 秒,可通过 page.set_default_timeout() 修改。
面试加分点:
- 对比 Selenium:Selenium 只等"元素存在",不等"元素可操作",所以经常需要手写
WebDriverWait。 - 提到这大大减少了 flaky test。
Playwright 的定位策略优先级是什么?¶
推荐优先级(从高到低):
| 优先级 | 方法 | 说明 |
|---|---|---|
| 1 | get_by_role() |
语义定位,最接近用户行为,推荐 |
| 2 | get_by_test_id() |
测试专用 ID,最稳定,需开发配合加 data-testid |
| 3 | get_by_label() |
通过 label 文本定位表单元素 |
| 4 | get_by_text() |
通过可见文本定位 |
| 5 | get_by_placeholder() |
通过 placeholder 定位 |
| 6 | locator("css=...") |
CSS 选择器,最后手段 |
不推荐用 XPath,因为可读性差、维护成本高。
Playwright 的网络拦截(route)怎么用?¶
常见场景:
# 1. Mock 接口响应
page.route("**/api/user", lambda route: route.fulfill(
status=200, body='{"name":"test"}'))
# 2. 阻止资源加载(加速测试)
page.route(re.compile(r"\.(png|jpg)$"), lambda route: route.abort())
# 3. 修改请求头
def add_auth(route):
route.continue_(headers={**route.request.headers, "Authorization": "Bearer xxx"})
page.route("**/api/**", add_auth)
# 4. 监听请求
page.on("request", lambda req: print(f"{req.method} {req.url}"))
面试加分点:对比 Selenium 需要第三方库(如 BrowserMob Proxy),Playwright 原生支持。
Playwright 的 Browser Context 有什么用?¶
Browser Context 是轻量级的浏览器会话,相当于一个独立的浏览器配置文件:
- 独立的 Cookie、LocalStorage、SessionStorage
- 独立的代理、地理位置、权限设置
- 共享浏览器进程,启动速度快
主要用途:
- 测试隔离:每个用例一个 context,互不影响
- 多用户模拟:一个浏览器进程模拟多个用户同时操作
- 登录态复用:通过
storage_state保存/恢复登录态
Playwright 的 Trace 怎么用?¶
Trace 是 Playwright 内置的调试工具,记录测试执行的完整过程:
- 每一步操作的截图
- DOM 快照
- 网络请求/响应
- 控制台日志
使用方式:
面试加分点:对比 Selenium 需要额外配置录屏或截图,Playwright 内置 Trace 可以逐步回放。
Page Object 模式怎么设计?¶
核心原则:
- 每个页面一个类,类中封装元素定位和操作方法
- 操作方法返回
self(链式调用)或返回下一个页面对象 - 用例层只调用 Page 方法,不直接操作元素
- 元素定位器集中在类顶部,修改时只改一处
class LoginPage:
def __init__(self, page):
self.page = page
self.username = page.get_by_placeholder("用户名")
self.password = page.get_by_placeholder("密码")
self.login_btn = page.get_by_role("button", name="登录")
def login(self, user, pwd):
self.username.fill(user)
self.password.fill(pwd)
self.login_btn.click()
return HomePage(self.page) # 返回下一个页面对象
Playwright 怎么做数据驱动测试?¶
推荐用 pytest.mark.parametrize:
@pytest.mark.parametrize("user,pwd,expected", [
("", "123456", "用户名不能为空"),
("test", "", "密码不能为空"),
("wrong", "wrong", "账号或密码错误"),
])
def test_login_validation(page, user, pwd, expected):
login_page = LoginPage(page)
login_page.open().login(user, pwd)
login_page.expect_error(expected)
也可以用 YAML/JSON 文件管理测试数据,通过 conftest.py 加载。
Playwright CI/CD 集成要注意什么?¶
- 用官方 Docker 镜像:
mcr.microsoft.com/playwright/python:vX.X.X-jammy - 安装浏览器依赖:
playwright install --with-deps chromium - 失败时上传报告:
actions/upload-artifact保存test-results/ - 并行控制:CI 环境 CPU 有限,适当限制 worker 数量
- 超时设置:CI 环境比本地慢,适当增加 timeout
- 视频/截图:CI 中调试困难,开启
--screenshot on --video on
Playwright 和 Selenium 的核心区别?¶
| 维度 | Playwright | Selenium |
|---|---|---|
| 架构 | 直连浏览器(CDP/自研协议) | WebDriver 协议(HTTP) |
| 自动等待 | 内置,等待元素可操作 | 需手动实现 |
| 网络拦截 | 原生支持 | 需第三方工具 |
| 速度 | 快(WebSocket 长连接) | 中等(HTTP 请求-响应) |
| 调试工具 | Trace Viewer、Codegen | 需额外配置 |
| 浏览器支持 | Chromium、Firefox、WebKit | 更广泛(包括 IE) |
| 生态 | 增长中 | 最成熟 |
| 学习曲线 | 平缓 | 中等 |
Playwright 的 Codegen 是什么?¶
Codegen 是 Playwright 内置的录制工具,可以录制用户操作并自动生成测试代码:
npx playwright codegen https://example.com # 打开录制器
npx playwright codegen --save-storage=auth.json # 保存登录态
适用场景:快速生成初始脚本、学习定位器语法、录制复杂操作流程。
注意:录制的代码通常需要人工优化,不适合直接用于生产。
Playwright 怎么处理 flaky test(不稳定用例)?¶
- 用
expect断言替代assert— expect 内置重试机制 - 用自动等待替代
sleep— Playwright 操作前自动等待元素可操作 - 用
--retries配置失败重试 — 需要pytest-rerunfailures - 用 Trace 录制失败现场 —
--tracing on-first-retry - 测试数据隔离 — 每个用例独立 Browser Context
- 等待网络请求完成 —
page.expect_response()确保数据加载完成 - Mock 不稳定的外部依赖 —
page.route()拦截第三方接口
Playwright 怎么做 API 测试?¶
Playwright 内置 APIRequestContext,不需要额外引入 requests 库:
# 方式 1:直接测试接口
def test_login_api(api_context):
response = api_context.post("/api/login", data={"user": "test", "pwd": "123"})
assert response.ok
assert response.json()["code"] == 0
# 方式 2:UI + API 混合(推荐)
def test_order_flow(page, api_context):
resp = api_context.post("/api/login", data={"user": "test", "pwd": "123"})
token = resp.json()["data"]["token"]
page.goto("https://example.com")
page.evaluate(f"localStorage.setItem('token', '{token}')")
page.reload()
面试加分点:一个框架搞定 UI + API 测试,不需要额外引入 requests 库。
Playwright 怎么调试测试?¶
三种调试方式:
| 方式 | 命令 | 适用场景 |
|---|---|---|
| Playwright Inspector | page.pause() |
逐步执行、查看元素 |
| 可视化模式 | pytest --headed |
看到浏览器实际操作 |
| Trace Viewer | playwright show-trace trace.zip |
回放失败现场 |
常用调试命令:
pytest --headed # 有头模式运行
pytest --browser chromium # 指定浏览器
pytest -k test_login # 只运行特定用例
pytest --screenshot on # 失败时自动截图
为什么不要大量使用 sleep?¶
推荐回答:
sleep 是固定等待:
- 页面快时 → 浪费时间
- 页面慢时 → 仍可能失败
更推荐:
- 显式等待:等待某个元素出现、可点击或接口完成
- 框架内置自动等待:如 Playwright 的 click() 自动等待元素可操作
元素定位不稳定怎么办?¶
处理思路:
- 优先使用稳定属性:
id、name、data-testid、可访问名称 - 避免依赖容易变化的层级 XPath
- 和开发约定测试专用属性
- 对动态列表先定位容器,再定位目标元素
- 失败时保存截图和页面 HTML
自动化用例失败怎么排查?¶
可以按顺序回答:
排查步骤:
1. 看失败日志和截图,确认失败步骤
2. 判断问题类型:
- 环境问题、数据问题
- 定位问题、等待问题
- 业务缺陷
3. 本地能复现 → 查看接口、数据库和页面状态
4. 只是偶现 → 检查并发数据污染或等待不稳定
自动化覆盖率是不是越高越好?¶
推荐回答:
面试自查¶
- 是否能解释自动化的价值和边界
- 是否知道稳定性问题怎么处理
- 是否能说出框架分层
- 是否知道失败排查流程
Page Object 模式是什么?有什么好处?¶
Page Object = 把每个页面的元素定位和操作封装成一个类,测试用例只调用类的方法
好处:
1. 元素定位集中管理,页面变化时只改一个地方
2. 测试用例更易读,调用的是业务方法而不是底层定位
3. 代码复用,同一个页面的操作可以在多个用例中复用
示例:
class LoginPage:
def __init__(self, page):
self.username = page.get_by_placeholder("用户名")
self.password = page.get_by_placeholder("密码")
self.submit = page.get_by_role("button", name="登录")
def login(self, user, pwd):
self.username.fill(user)
self.password.fill(pwd)
self.submit.click()
数据驱动测试怎么做?¶
数据驱动 = 把测试数据和测试逻辑分离,同一套用例用不同数据执行
实现方式:
1. Pytest 的 @pytest.mark.parametrize 装饰器
2. YAML/JSON/CSV 文件管理测试数据
3. 数据库读取测试数据
好处:
- 加新场景只需加数据,不改代码
- 数据和逻辑分离,易维护
怎么提高自动化用例的稳定性?¶
1. 用显式等待或自动等待替代 sleep
2. 用稳定的选择器(id、data-testid)替代易变的 XPath
3. 测试数据独立:每条用例用独立数据,避免互相影响
4. 前置准备和后置清理:运行前创建数据,运行后清理
5. 失败重试:对偶现失败用 pytest-rerunfailures 重试
6. 环境隔离:用 Docker 搭建独立测试环境
自动化测试怎么接入 CI/CD?¶
1. 代码提交后触发 Jenkins/GitHub Actions/GitLab CI
2. 流水线安装依赖、运行测试、生成报告
3. 用 Allure 生成可视化报告,归档到服务器
4. 测试失败时通知团队(邮件、钉钉、Slack)
5. 设置质量门禁:覆盖率低于阈值或有失败用例时阻断部署
接口自动化和 UI 自动化怎么选?¶
优先做接口自动化,原因:
- 速度快:接口请求毫秒级,UI 操作秒级
- 稳定性高:不受页面布局变化影响
- 覆盖广:可以测到 UI 无法覆盖的边界和异常
- 维护成本低:接口变化频率低于 UI
UI 自动化只覆盖核心用户路径:登录 → 浏览 → 下单 → 支付
你自动化项目的成功率/覆盖率是多少?¶
不要编一个精确数字。可以这样回答:
"核心接口用例 80+ 条,成功率稳定在 95% 以上。失败的主要原因是测试环境不稳定和测试数据被污染。
UI 自动化覆盖了 5 条核心流程(登录、搜索、加购、下单、支付),成功率 90% 左右。
我会定期分析失败用例,把环境问题和数据问题的用例优化掉。"
Python list 去重、切片、取倒数第N个元素?¶
列表去重:
list1 = [1, 2, 2, 3]
new_list = list(set(list1)) # 不保序
new_list = list(dict.fromkeys(list1)) # 保序
列表切片:
list1[1:4] # 下标1到3(不含4)
list1[:3] # 前3个
list1[2:] # 从下标2到末尾
list1[-3:] # 最后3个
取倒数第5个元素:
list1[-5]
面试加分:提到 set 去重不保序,dict.fromkeys 保序。
面试评分参考¶
| 维度 | 初级(1-2年) | 中级(3-5年) |
|---|---|---|
| 框架理解 | 知道 Page Object 和分层 | 能设计框架架构、选型 |
| 用例编写 | 能写基础 CRUD 用例 | 能处理数据驱动、并发、等待 |
| 稳定性 | 知道用显式等待 | 能分析失败根因、优化稳定性 |
| CI 集成 | 知道 Jenkins 跑测试 | 能搭建完整流水线+报告+通知 |