跨国家扩展测试数据听起来只是把样本从几个加到几十个,实际做起来会发现成本不在数据量,而在规则。每新增一个国家和地区,都可能带来一条新的例外:没有邮编、行政区层级更浅、编码只在部分体系里有值。没有事先分好的不变量与例外清单,扩展就会变成一次次的临时补丁。
从几个国家扩到几十个,难在哪?
难点有三个层次,成本依次递增。
第一层是数据量:更多的地址、更多的名称、更多的行政区。这一层是体力活,靠收集与整理就能解决。
第二层是组合:国家和地区与语言、与支付方式、与配送方式相乘,组合增长远快于样本数增长。
第三层是规则分歧:原本在一两个国家和地区上成立的假设,到第十个就不成立了。这一层最贵,因为它会影响已经写好的逻辑,而不是只增加数据。
多数团队在第二层就开始吃力,真正卡住的是第三层。而第三层恰恰是可以提前准备的,前提是先有一份不变量与例外清单。
扩展的前提是国家集合本身稳定;这份集合一旦反复变动,前面所有的断言都要跟着重算。
不变量:在所有国家和地区都成立的规则
不变量是那些无论扩展到多少个国家和地区都不需要改的规则。它们通常很朴素,例如:
- 每个条目都有一个稳定的内部标识。
- 一条地址至少属于一个国家和地区。
- 每个条目的状态是有限取值之一。
- 未选择的条目不应被当成有效值写入。
不变量的价值在于:它们可以用属性方式测试——不需要枚举输入,只需要断言「无论哪一条数据,这个性质都成立」。这类断言写一次就能覆盖全部数据,扩展时零成本。
不变量还有一个作用是止损:当某条断言在新数据上失败时,你立刻知道要么是数据错了,要么是这条不变量定得太宽,而不必在两者之间反复猜。
写不变量时要克制。凡是需要举例才能说清的,多半不是不变量而是常见情况;把它硬当成不变量,第一次遇到例外就会全线报错,反而会让人不再相信这套断言。
不变量与例外合起来才描述得清数据,而覆盖率清单正好是记录两者边界的地方,见国家数据覆盖率清单。
例外清单应该记什么?
例外清单要记的是「哪些国家和地区不遵循哪一条常见规则」,而不是「有哪些奇怪的国家和地区」。
每一条例外至少包含四项:涉及哪个条目、偏离了哪一条规则、为什么偏离、以及从什么时候起有效。
只写条目名的清单没有用,因为它不告诉你该跳过哪一步校验;只写规则名的清单也没有用,因为执行时不知道该对谁网开一面。
例外还需要有生命周期。如果某条例外对应的原因消失了,它应该被删除,而不是永远留着。清单如果没有清理机制,只会在几年后变成谁也不敢动的历史遗迹。
还有一个反直觉的建议:例外清单应该尽量短。例外太多的系统,通常说明不变量定错了——把某条常见情况误当成了普遍规律。
数据驱动与代码分支怎么选?
面对国别差异,有两种写法,各有适用场合。
代码分支是把差异写进逻辑:如果属于某类国家和地区,就走另一套流程。它直观、调试方便,适合数量少且几乎不会变的差异。
数据驱动是把差异写成配置:规则、字段集合、必填性都从数据里读。它扩展成本低,新增一个国家和地区通常不需要改代码,代价是配置本身也会变得复杂,而且问题会从编译期推迟到运行期。
实践中的分界大致是:结构性的、几乎不动的差异用分支;随国家和地区数量增长的差异用数据驱动。
有一条硬性判据很有用:新增一个国家和地区时要不要改代码。如果每次都要改,说明这部分应该被数据化;如果几乎不用改,说明当前的选择是对的。校验规则本身的组织方式在跨主题里有更一般的讨论,见号码校验是怎么工作的。
扩展时先加国家还是先加字段?
这个顺序会显著影响返工量。
先加国家和地区的做法能快速暴露规则分歧,适合探索阶段:你会在很短的时间里知道哪些假设不成立。
先加字段的做法能把结构先搭好,适合结构已经清楚、只是要补维度的情况:边界清晰之后,补字段是线性的工作。
如果两者都不确定,建议小步交替:一次只加少量国家和地区,同时只加少量字段,观察哪一侧先出问题。这样每一轮的结论都能被解释,而不是在一堆同时变化的条件里找原因。
无论哪种顺序,都要给每一轮留一份记录:这一轮加了什么、发现了哪条新例外、哪条旧例外被删掉了。这些记录本身就是最重要的产出,因为下一轮要接着用。
给开发者:让新增一条数据不需要改代码
把扩展成本压下来的关键,是让新增一条数据成为纯数据操作。
条目要有稳定的内部标识,外部编码作为属性,这样编码体系变化时数据结构不用动。
规则要能按属性组合出来,而不是按条目名列举。必填、格式、可选值都应当是由属性推导的结果。
例外的表达要有位置:它可以是一条带生效时间的记录,也可以是给条目打的标记,但不能是散落在逻辑里的条件判断。
变化要能被观测:新增一条数据之后,能用一次查询确认它落进了哪些规则分支、命中了哪些例外。
做到这几点,扩展就从「改代码加测试」变成「加数据跑断言」。而数据本身也会过期,这套时间维度上的维护见国家数据新鲜度。
要对照着看国家和地区条目实际有哪些属性,国家和地区总览是一个现成的入口。
下一步
先把现有断言按不变量与例外分一遍:哪些是无论加多少国家和地区都成立的,哪些只是当前样本凑巧成立的。再把例外整理成带生效时间的清单,删掉已经没有原因的那些。
做完这一步,下一轮扩展就会从「又踩到一个坑」变成「又验证了一条不变量」。
文中的不变量条目与例外清单都是为演示组织方式而写的示例结构,不代表任何真实系统的断言全集,也不构成对任何具体校验实现的描述。