Go の omitempty はゼロ値フィールドをJSON出力から丸ごと消すため、レスポンス観測型の契約テストは検知方式を問わず盲目になる
struct field に json:"...,omitempty" を付けた場合、そのフィールドがゼロ値(空文字・0・nil・空スライス等)のままだと、encoding/json はキーごとJSON出力から省略する。DTOへ新しいフィールドを追加しても、値を組み立てる側(コンストラクタ/presenter)で非ゼロ値を配線しない限り、レスポンスJSONにそのフィールドの痕跡は一切現れない。
この結果、レスポンスを外側から観測する契約テストは、検知手法を変えても効果が変わらない。文字列非包含チェック(特定のキー名がボディに含まれないことを表明する)でも、JSONをmapへデコードしてキー集合の完全一致を取る許可リスト方式でも、出力バイト列が変異前後で完全に同一になるため、どちらも検知できない。「より厳密な検証方式に置き換えれば防げるはずの穴」に見えて、実際には防げない。
検証方法: 対象のDTOにフィールドを追加し、(a) omitempty付き・値は未配線(ゼロ値)のまま、(b) omitemptyを外す、(c) omitemptyは維持したまま実際に非ゼロ値を配線する、の3パターンでレスポンスJSONの差分を比較する。(a)だけが変異前後で出力が同一になり、(b)(c)はどちらの検知方式でも検知できる。
実務上の含意: 「新しいフィールドを追加しても既存の許可リスト検査で守られる」という期待は、そのフィールドが将来ゼロ値のまま出荷される可能性がある限り成立しない。この種の退行を本当に防ぎたいなら、レスポンス観測ではなく、DTOのstruct定義そのものを静的に検査する(許可されたフィールド一覧との照合、reflectによるタグ列挙など)方式を別途組み合わせる必要がある。ただし静的検査はDTOをstructから組み立てない経路(map直返し等)には効かないため、両方式はトレードオフの関係にあり、片方だけで完全な検知にはならない。
適用範囲: Go以外でも、シリアライズ時に「値が空なら出力しない」という指定を持つ仕組み全般(JavaScriptの独自replacer、JacksonやGsonのNON_EMPTY/NON_DEFAULT系include設定等)に一般化できる可能性がある。「値が空のフィールドを省略する」直列化オプションを使っている箇所では、レスポンス観測型のテストがこの種の退行に対して原理的に無力になりうる、という前提でテスト設計を見直す価値がある。