Resume
📌 Q:最有成就感的项目
做了什么事情、总结沉淀、工具等?遇到什么难点?
A:面试官您好,我讲一下广东移动渠道费用管理系统这个项目。
这个项目是给运营商合作的渠道做业务结算的,比如营业厅、空中充值代理点、中小企合作商等。我负责从数据采集、清洗、计酬、合账报账的全流程质量保障。这里面我遇到了四个核心难点:
- 第一个难点:多平台数据采集与 ETL 清洗的质量保障。
数据从外围系统(文件、库表)采集到 Hadoop,经过批次清洗后再回流到 Oracle。我做的不是只测最终结果,而是在每个清洗批次后写 SQL 做数据对账,比如源表行数 vs 清洗后行数、关键字段缺失率,发现批次逻辑错误就立刻定位到具体清洗脚本。这让我在早期就拦截了多次数据丢失问题。
- 第二个难点:基于 Redis + Groovy 规则引擎的计酬逻辑测试。
计酬规则配在前端菜单,规则是流程图,因子里的逻辑是用 Groovy 写的代码,分支非常多。纯黑盒测试根本覆盖不全边界场景。
我做的是灰盒测试:先去读 Groovy 代码,梳理出每个因子的判断分支,再用 Python 脚本批量构造覆盖所有分支的边界数据,灌入 Redis 后触发计酬任务,最后比对数据库结果。这样把规则分支覆盖率从不到 40% 提升到了 90% 以上。
- 第三个难点:报账环节与外围系统交互的自动化 Mock。
报账时要向外围系统发报文(XML 格式),之前测试人员手动操作容易漏检查报文内容,导致线上报错。我开发了一套 Flask + paramiko 的 Mock 服务:
用 Flask 接收应用发来的 JSON 格式报文,直接解析校验。
用 paramiko 从远程服务器拉取 XML 报文文件,写了一个递归解析函数对比两边的标签和属性值,确保报文内容完全正确。
踩坑:Flask 启动会阻塞主进程。我的解决办法是用 Flask 自带的 WSGI 服务器(werkzeug.serving.run_simple)在子线程中启动,而不是用 os.system 那种笨办法。这套 Mock 让报账环节的回归时间从半天压缩到了 30 分钟。
- 第四个难点:权限中心 50+ 菜单的 UI 自动化回归。
权限中心早期代码耦合严重,改一个菜单可能影响所有财务相关菜单,每次回归要投入十几个测试人员跑 200 多个用例。
我基于 Playwright + POM 模式搭建了 UI 自动化框架。选 Playwright 而不是 Selenium 的原因是:它默认自动等待、支持多浏览器、调试工具更友好。最终覆盖了 50+ 核心页面,自动化通过率稳定在 97% 以上,把回归时间从 2 天压缩到 2 小时。
总结一下,这个项目让我在 ETL 数据质量、规则引擎灰盒测试、Mock 服务开发、UI 自动化落地 四个方面都有了比较扎实的实战经验。
📌 Q:如何保障跨系统的数据一致性?对账脚本如何设计?
“由粗到细、由面到点”
🛠️ 跨系统数据一致性校验脚本设计思路:
1.宏观对账:元数据与聚合统计 (Metadata Check)
- 总量对比 (Count): SELECT COUNT(*) 是否相等。
- 关键指标对比 (Sum/Avg): 针对金额、积分、数量等字段做 SUM(balance) 。如果总量对但总金额不对,说明发生了“数据偏移”或“精度丢失”。
- 边界值对比 (Min/Max): 对比最大/最小 ID 或更新时间,确认同步范围是否一致。
2.分批哈希对比 (Batch Hash Check)
核心点:为提高效率,引入 “递归下钻” 的思路:
- 分批/分片策略: 按照主键 ID 取模或按创建时间分片(例如:每 10 万条数据一个 Bucket)。
-
哈希计算逻辑:
- SQL 端计算(推荐):尽可能让数据库去计算。例如 MySQL 可以用 MD5(CONCAT_WS(',', col1,col2...)) 。这样脚本只需要拉取一个 32 位的字符串,极大地节省网络IO。
-
字段处理原则:
- 排序: 必须 ORDER BY ID,否则哈希值必不一致。
- Null 处理: 统一将 Null 转为空字符串。
- 精度处理(坑): 跨平台时(如 Oracle 转 Hadoop),浮点数(1.5 vs 1.50)会导致哈希失败。必须在 SQL 层面统一 ROUND(col, 2)。
-
故障定位(下钻):
- 如果 Batch_01 的 MD5 对不上,将该批次再细分为 10 个小批次,递归定位,直到锁定具体的几个 ID,最后只对这几个 ID 进行全字段打印对比。
3.动态抽样对比 (Sampling Check)
用于日常监控或数据量极大的场景。
- 随机抽样: 每天随机抽取 1% 的数据进行全字段对比。
-
权重抽样(更专业):
- 针对 “高频更新” 的数据(根据 update_time)。
- 针对 “异常状态” 的数据(状态位为‘失败’或‘重试中’的记录)。
- 针对 “大额交易” 的数据。
其他方式对账
- 通过数据质量监控工具,如 Kettle 、阿里开源的 DataX 等。
- Spark / Scala 分布式对账,处理亿级数据。
- Shell + 命令行工具,使用 Hive 的 beeline -e "sql" 和 Oracle 的 sqlplus 将结果导出为文本文件,再 diff 对比。
🚁 Q:批次处理断点续跑如何测试?测试时怎么制造失败场景?失败后怎么判定续跑是否有效?
内部设计:进度检查点保存到Redis + DB(双写)
如何测试,并确保续跑有效性:
-
冒烟验证主功能
-
验证数据一致性:
- 正常跑和断点续跑数量一致
- 下钻,全量对比或抽断点附近的记录进行对比
-
-
断点状态验证:检查断点状态,验证断点续跑是否从断点位置开始,而不是重跑
- 幂等验证
- 多线程/并行验证:若程序内部是多线程方式实现,崩溃后,检查其他线程是否也从各自断点恢复,而不是互相干扰。
如何制造失败场景:
- 随机异常注入:代码里埋故障点,按概率触发抛异常
- 线程中断:模拟 Ctrl+C / kill -9
- 脏数据:源表插入格式异常的记录
- DB断连:暂停db容器
📌 Q:如何保障分布式事务一致性(跨微服务)?
在“支付-扣减-发货”这种分布式链路中,核心思路是“不仅测成功的路径,更要测失败后的补偿逻辑”。
不追求强一致性(ACID),而是追求最终一致性(BASE)。
1.异常中断与补偿测试(核心):
- 场景:支付成功了,但‘权益下发’服务挂了或超时了。
- 测试点:验证系统是否具备自动重试机制。如果重试 N 次仍失败,是否进入了死信队列(DLQ)或生成了异常工单供人工介入。
- 操作:在测试环境通过 Mock 掉权益接口返回 500 或超时,观察支付服务的补偿逻辑。
死信队列(DLQ)
消息队列中用于存储无法被正常消费的消息,以便后续分析、重试或人工处理。
消息成为“死信”的常见原因 - 消息被消费者拒绝(如 basic.reject 或 basic.nack,且 requeue=false)。 - 消息过期:设置了 TTL(生存时间),超时未被消费。 - 队列达到最大长度:当队列满时,最早的消息会被移入死信队列。 - 消息被重试次数超过预设上限(依赖具体实现)。
死信队列的作用
- 故障隔离:避免坏消息阻塞主队列的正常消费。
- 问题排查:开发/运维人员可以从死信队列中提取消息,分析失败原因(如格式错误、依赖服务不可用等)。
- 消息补偿:修复问题后,可将死信队列中的消息重新投递到原队列或另做处理。
- 保证数据不丢失:防止消息因消费失败而被永久丢弃。
2.接口幂等性验证:
- 场景:MQ 消息因为网络抖动可能会重发。
- 测试点:权益下发服务如果收到两条同样的‘支付成功’消息,是否会发两次权益?
- 方案:使用 JMeter 并发发送相同
order_id的请求,或者手动在数据库层面模拟重复 MQ 消息,校验数据库记录是否唯一。
3.状态机闭环校验:
- 方案:编写自动化脚本实时扫描数据库,校验是否存在‘支付已成功’但‘权益状态’超过 5 分钟仍为‘未处理’的异常订单。这属于实时对账监控。”
📌 Q10:为什么要用 Playwright 而不是 Selenium ?
根据项目现状,有大量 Selenium 脚本稳定运行则不动;新项目可考虑 Playwright 。
-
Playwright 等待机制更稳定
- Selenium 二次开发时需要先封装 WebDriverWait ,避免元素未就绪导致的异常,当元素可见但被另外 loading 遮罩挡住时依旧会抛异常。
- Playwright 自带等待的机制会等待遮罩消失,除非超时的情况。解决方案:手动加 wait_for_selector 等待遮罩隐藏。
-
Playwright 调试方法更完善
- Selenium 失败后需依靠代码进行的截图和日志,排查效率差:比如复现需要先限速、控制台
setTimeout(()=>{debugger;},1000)(1秒后冻结浏览器)的步骤定位覆盖的元素。 - Playwright Trace Viewer 可以录制整个执行过程的 DOM 快照、网络请求、控制台日志等结构化信息,用例失败后直接看回放,能快速定位到是元素没出来还是接口报错。
- Selenium 失败后需依靠代码进行的截图和日志,排查效率差:比如复现需要先限速、控制台
# Trace Viewer 调试命令
pytest test_xxx_manage.py -s --tracing=retain-on-failure
playwright show-trace xxx_manage.zip
- Playwright 无需配置浏览器驱动
🚁 拓展
- 支持并行执行
- 支持多个浏览器上下文
- 支持移动设备模拟
- 支持视频录制(mp4)
- 内置html格式报告,也可无缝集成allure报告
- 网络拦截和 Mock 能力强大
Selenium 需要借助代理工具才能 mock 接口。Playwright 原生支持 page.route("/api/**", handle_route) 拦截指定请求并返回自定义响应,不用另起代理服务,代码量和维护成本低。
应用场景
- 后端未就绪时,Mock 接口让前端先测,缩短依赖等待。
- 模拟异常响应,测试前端容错,比如 500(
route.fulfill(status=500))、网络中断(route.abort())、超时、非正常 JSON。 - 消除不稳定依赖,比如微信支付、短信接口,避免 CI 因为第三方挂掉而失败。
- 快速模拟角色权限,同一个账号通过 Mock 不同的权限返回,测试多角色 UI 展示,提高回归效率。
- 绕过验签/防刷逻辑,聚焦业务测试,如绕过真实验证码?
- 模拟慢网速、弱网环境:
route.fulfill(delay=3000)
📌 APM 数据看板,有现成工具为什么还重复开发?
- 数据清洗:排除健康检查、探活、静态资源,通过聚合到同一个大屏,解决数据孤岛问题
- 通常默认保留原始Trace数据仅7-15天(存储成本极高),过期即删。
定时清洗后将聚合指标(分钟级)存入PostgreSQL,可以长期保留