跳转至

自动化测试面试题

自动化测试面试不会只问“会不会 Selenium”。更常见的是问你为什么做自动化、怎么选场景、怎么保证稳定、失败怎么排查。

什么样的用例适合自动化?

推荐回答:

适合自动化的是稳定、重复执行、业务价值高、人工执行成本高的场景,比如登录、下单、查询、核心接口回归。
不适合自动化的是需求频繁变化、一次性验证、强依赖人工判断或验证码很复杂的场景。

自动化测试框架一般包含哪些模块?

可以回答:

  • 用例层:组织测试用例。
  • 请求或页面封装层:封装接口请求或页面操作。
  • 数据层:管理测试数据。
  • 配置层:环境地址、账号、超时时间。
  • 断言层:判断结果是否符合预期。
  • 报告层:生成 Allure、HTML 等报告。
  • 日志层:记录失败原因。
  • CI 集成:在流水线中自动运行。

Selenium 和 Playwright 怎么选?

如果公司已有 Selenium 框架,我会优先维护现有框架,因为迁移成本高。
如果是新项目,我更倾向 Playwright,因为它自动等待能力强,调试工具和 trace 对定位问题比较友好。
但最终要看团队技术栈、浏览器要求和维护成本。

Playwright 的自动等待机制是怎么工作的?

回答思路:先说原理,再对比 Selenium。

Playwright 在执行操作前会自动等待元素满足 5 个条件:

  1. 元素存在于 DOM 中
  2. 元素可见(非 display:nonevisibility:hidden
  3. 元素稳定(不在动画中)
  4. 元素未被遮挡
  5. 元素启用(非 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
  • 独立的代理、地理位置、权限设置
  • 共享浏览器进程,启动速度快

主要用途:

  1. 测试隔离:每个用例一个 context,互不影响
  2. 多用户模拟:一个浏览器进程模拟多个用户同时操作
  3. 登录态复用:通过 storage_state 保存/恢复登录态

Playwright 的 Trace 怎么用?

Trace 是 Playwright 内置的调试工具,记录测试执行的完整过程:

  • 每一步操作的截图
  • DOM 快照
  • 网络请求/响应
  • 控制台日志

使用方式:

# pytest.ini 配置
--tracing on

# 用例失败后查看
playwright show-trace trace.zip

面试加分点:对比 Selenium 需要额外配置录屏或截图,Playwright 内置 Trace 可以逐步回放。

Page Object 模式怎么设计?

核心原则:

  1. 每个页面一个类,类中封装元素定位和操作方法
  2. 操作方法返回 self(链式调用)或返回下一个页面对象
  3. 用例层只调用 Page 方法,不直接操作元素
  4. 元素定位器集中在类顶部,修改时只改一处
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 集成要注意什么?

  1. 用官方 Docker 镜像:mcr.microsoft.com/playwright/python:vX.X.X-jammy
  2. 安装浏览器依赖:playwright install --with-deps chromium
  3. 失败时上传报告:actions/upload-artifact 保存 test-results/
  4. 并行控制:CI 环境 CPU 有限,适当限制 worker 数量
  5. 超时设置:CI 环境比本地慢,适当增加 timeout
  6. 视频/截图: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(不稳定用例)?

  1. expect 断言替代 assert — expect 内置重试机制
  2. 用自动等待替代 sleep — Playwright 操作前自动等待元素可操作
  3. --retries 配置失败重试 — 需要 pytest-rerunfailures
  4. 用 Trace 录制失败现场 — --tracing on-first-retry
  5. 测试数据隔离 — 每个用例独立 Browser Context
  6. 等待网络请求完成 — page.expect_response() 确保数据加载完成
  7. 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() 自动等待元素可操作

元素定位不稳定怎么办?

处理思路:

  • 优先使用稳定属性:idnamedata-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 跑测试 能搭建完整流水线+报告+通知