消费者与提供者之间的假设写在哪里?这篇用一个很小的订单接口量出:消费者的桩测试对提供者的 8 个版本给出了多少信息,契约能拦住什么、拦不住什么,以及 ObjectMapper 的宽松默认值如何把契约违反变成静默的错误值。
消费者单元测试(手写桩 = v1 JSON):通过 —— 桩的内容不会随提供者变化 v1 基线 解析成功且值正确 id=7 status=PAID total=1999 note=gift v2 total 改名 amount 抛异常 UnrecognizedPropertyException: Unrecognized field "amount" (class Main$Order), not marked as ignorable (4 known propertie… v3 total 变成字符串 解析成功且值正确 id=7 status=PAID total=1999 note=gift v4 新增 currency 抛异常 UnrecognizedPropertyException: Unrecognized field "currency" (class Main$Order), not marked as ignorable (4 known propert… v5 删除 note 解析成功且值正确 id=7 status=PAID total=1999 note=null v6 新增枚举值 抛异常 InvalidFormatException: Cannot deserialize value of type `Main$Status` from String "REFUNDED": not one of the valu… v7 total 变成小数 解析成功但值不对 id=7 status=PAID total=19 note=gift v8 total 为 null 解析成功但值不对 id=7 status=PAID total=0 note=gift 合计:值正确 3,抛异常 3,解析成功但值不对 2
v1 基线 契约通过 v2 total 改名 amount 契约失败:total 缺失 v3 total 变成字符串 契约失败:total 应为 integer,实际 STRING v4 新增 currency 契约通过 v5 删除 note 契约通过 v6 新增枚举值 契约失败:status 值 "REFUNDED" 不在 NEW|PAID|SHIPPED v7 total 变成小数 契约失败:total 应为 integer,实际 NUMBER v8 total 为 null 契约失败:total 应为 integer,实际 NULL 合计:通过 3,失败 5
默认 ObjectMapper:抛异常 UnrecognizedPropertyException: Unrecognized field "currency" (class Main$Order), not marked as ignorable (4 known propert… FAIL_ON_UNKNOWN_PROPERTIES=false:id=7 status=PAID total=1999 note=gift 契约验证 v4:通过(契约只约束消费者用到的字段,不禁止多出的字段)
v2 total 改名 amount 宽松:成功 total=0
严格:抛异常 MismatchedInputException: Missing creator property 'total' (index 2); `DeserializationFeature.FAIL_ON_MISSING_CREATO…
v3 total 变成字符串 宽松:成功 total=1999
严格:抛异常 MismatchedInputException: Cannot coerce String value ("1999") to `int` value (but might if coercion using `CoercionC…
v7 total 变成小数 宽松:成功 total=19
严格:抛异常 InvalidFormatException: Cannot coerce Floating-point value (19.99) to `int` value (but could if coercion was enabl…
v8 total 为 null 宽松:成功 total=0
严格:抛异常 MismatchedInputException: Cannot map `null` into type `int` (set DeserializationConfig.DeserializationFeature.FAIL_O…消费者 A 的契约 vs v5:通过 消费者 B 的契约 vs v5:失败:note 缺失 默认 ObjectMapper 解析 v5:id=7 status=PAID total=1999 note=null(note 缺失被当成 null,没有报错) 不看消费者、只比较 v1 与 v5 的完整字段:删除了 [note] → 一律判为破坏性变更
默认:抛异常 InvalidFormatException: Cannot deserialize value of type `Main$Status` from String "REFUNDED": not one of the valu… READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE + @JsonEnumDefaultValue UNKNOWN:成功 status=UNKNOWN total=1999 契约验证 v6:status 值 "REFUNDED" 不在 NEW|PAID|SHIPPED