嘉为科技测试开发秋招面经:接口与 UI 自动化框架设计
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 如何设计一套支持多环境、多账号并发执行的接口自动化测试框架?
- 接口自动化中,上一个接口返回的数据要传给后续多个接口,如何管理上下文?
- 如何搭建一套可维护的 UI 自动化测试框架?
- 请做一下自我介绍
- 介绍一下你上一段实习经历
《参考解析》
框架分层:把环境、账号、数据都从代码里赶出去
分层的价值不在层次多,而在每一层只解决一类变化。配置层管环境地址、数据库连接、账号池,用 pydantic-settings 或 YAML + 环境变量覆盖,启动时用 pytest --env=staging 或 TEST_ENV=staging 选一份配置,代码里出现 https://xxx.com 这类字面量就应该在 CI 里直接失败。请求层包一个 ApiClient,持有 requests.Session 复用连接、统一注入 Authorization、统一打请求/响应日志,并且强制带超时 timeout=(3, 10)(连接 3 秒、读取 10 秒),不写超时的用例等于把挂死风险留给 CI。数据层负责造数和清数,用 uuid4().hex[:8] 或「worker 名 + 时间戳」拼唯一后缀,避免两个线程抢同一个订单号。用例层只描述业务流程,断言层单独抽出来同时校验三处:HTTP 响应码与响应体字段、数据库里的落库状态、异步链路的消息或任务状态。报告层交给 pytest-html / Allure,同时输出 JUnit XML 给流水线解析。
多账号并发:隔离粒度决定框架能不能横向扩
并发用 pytest-xdist -n 8 起步,瓶颈出现再加。关键约束是同一个账号不能同时被两个 worker 用:常见做法是准备账号池,用文件锁或 Redis 队列做借还,worker 从 os.environ["PYTEST_XDIST_WORKER"] 拿自己的编号去分片;也可以让每个 worker 登录一次、把 token 缓存在自己进程内,绝不要写到共享文件里被互相覆盖。数据侧同理,要么按 worker 分配独立租户/库前缀,要么每条用例自带唯一业务键。另外注意 xdist 的调度单位是测试文件,同一个类里的用例通常落在同一 worker 上,串行依赖放在同一个用例里而不是靠执行顺序。
上下文传递:用场景级对象,不要用全局变量
跨接口传数据最稳的是每个测试流程创建一个上下文对象(dataclass 即可),字段放 token、业务主键、动态参数和待清理资源;用 function 作用域的 fixture 创建,用例结束自动销毁 —— 全局字典在并行下必然互相覆盖。取值时把「断言」和「提取」分开:先校验 resp.status_code == 200、再用 JSONPath 或 resp.json()["data"]["taskId"] 提取,提取失败要抛错而不是返回 None 让后续接口拿着空值跑出一堆误导性失败。前置步骤挂了就立刻终止该流程(pytest.skip 要慎用,失败就该是失败,或用 pytest-dependency 标记依赖),否则下游会出现几十条级联失败,既掩盖真因又拖长回归时间。清理动作按栈逆序记录在上下文里,fixture 的 teardown 阶段逐条执行;注意 teardown 在用例失败时同样会跑,所以清理本身要写成幂等、能容忍「资源本来就没建成功」。
重试边界:有副作用的接口默认不重试
请求层做统一重试是最容易埋雷的地方。urllib3 的 Retry(total=3, backoff_factor=0.5, status_forcelist=[502, 503, 504]) 默认只对安全方法生效,一旦你把 POST 放开,一次网关超时就可能变成两笔订单。正确姿势是:查询类接口(GET/HEAD)随便重试;创建、支付、扣款这类写接口要么完全不自动重试,要么请求携带幂等键(Idempotency-Key 或业务唯一 requestId)并由服务端保证重复请求返回同一结果。超时之后的处理不是盲目重发,而是先调查询接口确认「到底建没建成」,再决定重试还是继续 —— 这是查证式补偿,不是重试。
UI 自动化:Page Object 管操作,测试用例管验证
UI 层用 Pytest 驱动,Page Object 只封装「怎么操作」,断言留在用例里,这样页面改版时改动收敛在页面类内部,测试意图也不会被页面细节淹没。等待一律用显式等待 WebDriverWait(driver, 10).until(EC.element_to_be_clickable(locator)),禁止 time.sleep;也不要隐式等待和显式等待混用,两者叠加会让实际等待时长变得不可预测。定位优先和前端约定 data-testid,比 XPath 层级稳定得多 —— CSS 选择器 [data-testid='create-task'] 这种写法重构样式时不会碎。跨页面流程(登录、下单)再往上封一层业务动作,让用例读起来像业务语言。
失败现场的采集能力比断言本身更影响排查效率:挂 pytest_runtest_makereport 钩子,在 call 阶段失败时保存截图、driver.page_source、浏览器控制台日志(goog:loggingPrefs 打开 browser 日志)和网络请求记录,一起塞进 Allure 附件。只报一句「元素找不到」,别人拿到报告还得本地复现一遍。稳定性上,pytest-rerunfailures --reruns 1 可以用来救偶发,但必须单独统计首跑通过率而不是最终通过率,被 rerun 救回来的用例要标记并进入隔离清单,长期挂着重跑等于把 flaky 当正常。
自我介绍与实习经历:用数字和边界讲,别念简历
自我介绍控制在 60 秒内:身份与方向 → 一个最匹配测试开发的经历 → 量化结果。测试岗的量化指标现成就有:覆盖了多少接口/页面、用例多少条、回归耗时从多少降到多少、发现的有效缺陷数、线上漏测率。实习经历按「问题—动作—结果」讲,重点讲一次你自己定位线上问题的完整链路(看日志 → 抓包比对 → 查数据库确认数据状态 → 缩小到某个参数或并发场景),面试官追问的通常就是这一段的细节:模块边界在哪、哪些不是你做的、当时为什么选这个方案。没做过的不要写进简历,测试岗的追问密度很高,一句「不太清楚」会连带把前面讲的可信度一起削掉。