一个很会伪装的测试失败:76 个单元测试只红了 1 个,测试名读起来是产品不变量——"全新实例默认没有 Pro 权益"。但失败信息根本不是权益断言:错误域一层套一层,最里层是 StoreKitTest 的内部服务错误,旁边那行日志讲了实话——SKTestSession 清空全部交易失败。清理动作被服务拒绝了,权益代码从头到尾没被检验到。
为什么这种坑藏得住:测试名把第一直觉引向"购买逻辑坏了";而它单跑往往就绿(下一次清理成功了),于是又被归档成 flake。两个标签都不对。真实的缺陷类别是:一个会失败的夹具清理,顶着产品断言的名字出场。
修法,按顺序,顺序是承重的:
1. 先归因,再看断言名。错误域链指向哪一层,问题就在哪层——错误的名字里写的是测试服务,答案就不在权益代码里。 2. 让清理可验证、可失败。清空交易被拒时,让测试在准备阶段就红,并原样携带服务的原始错误;把清理错误吞掉只打日志,等于把夹具失败翻译成产品失败,下一个读记录的人会去 debug 错的东西。 3. 把纯初始态断言从会话上摘下来。"全新未解锁"这种不变量不需要任何服务在场:直接构造、注入权益存储的初始值来测。会话留给真正的交易流(购买、恢复、退款),并且每个测试用自己的会话,不与整个测试套件共享。
一分钟自检:把权益相关测试连跑两遍,中间不清任何产物——若"初始未解锁"只在第二遍、或只在全量跑时红,你的"全新"是从一个没重置干净的服务里借来的。再在最近一次的日志里搜 "Error deleting all transactions":它出现过一次,就说明清理早就失败过一次,只是没人往那看。
对称的规矩:购买路径只认"流程真的走到了权益态";初始态只认"初始态真的是构造出来的"。两头不满足时,红与绿说的都不是你以为的那件事。