「完成した」と思っていたMERNスタックのZerodhaクローンに対してSpecmaticの契約テストを実行した瞬間、手動テストでは決して見つけられなかった5つの実在する欠陥がツールによって指摘されました。生成された178個のテストケースのうち、テストスイートは、無効なデータ型、不正な認証情報によるクラッシュ、サイレントなデータ破損、非冪等(non-idempotent)なサインアップ、そしてCIゲートを即座に停止させた破壊的な契約変更など、実際のユーザーに影響を与えていたであろう方法でAPIを破壊しました。
なぜ手動テストをすり抜けてしまったのか
このプロジェクトは、Node.jsのバックエンド、Reactのフロントエンド、MongoDBのストレージ、そして決済用のRazorpayを組み合わせて構成されています。サインアップと決済のフローを手動で一通り確認しましたが、すべて正常に動作しているように見えました。しかし、手動テストは「ハッピーパス(正常系)」しか検証しません。つまり、ユーザーが意図した通りの手順を踏んだ場合にコードが正しく動作することを確認するだけなのです。不正なリクエストや予期しないクライアントの挙動に対してもサービスが耐えられるかどうかを証明することはできません。
Specmaticを既存のコードに向けたとき、各エンドポイントのリクエストとレスポンスの形状を明示的に記述した「契約(contract)」が、信頼できる唯一の情報源(source of truth)として機能しました。ツールは、人間がテストを書こうと思ってもまず思いつかないような、ポジティブ(正常系)およびネガティブ(異常系)シナリオの膨大なマトリックスを自動生成しました。
発見された5つの欠陥
- 入力バリデーションの不備 –
/newOrderエンドポイントは、契約では整数(integer)が求められていたにもかかわらず、quantityフィールドに小数や文字列を受け入れてしまいました。誤った型を送信する生成テストによって、APIが予期しない動作を引き起こしました。 - 未処理のログインエラー – 認証ルートに不正な形式の認証情報を送ると、コードに型チェックが欠けていたため、実行時例外(runtime exception)が発生しました。
- サイレントな決済データの破損 –
/verify-paymentエンドポイントは、amountにブール値(boolean)を許可していました。trueが紛れ込んだ際、データベースは決済額ゼロの成功として記録してしまい、収益データが密かに水増しされる結果となりました。 - 冪等性の欠如 – テストスイートでサインアップフローを2回目に実行した際、エンドポイントが重複を適切に処理せず、既存のユーザーを再作成しようとしたため、エラーとなりました。
- 破壊的な契約変更の検知 – 意図的に契約内のデータ型を変更したところ、CIパイプラインが即座にその変更を拒否し、破壊的なリリースを防ぐことができました。
CIパイプラインにおいて契約テストが重要な理由
- 大規模なネガティブテスト – 178個のケースのほとんどはエッジケースの入力でした。これらをすべて手動で書くのは、現実的ではないほど時間がかかります。
- サードパーティ製クライアントへの安全性 – 契約は、サービスが外部のコンシューマーに対して何を約束するかを定義します。実装が契約から逸脱した場合、契約テストが失敗することで、ダウンストリームのアプリケーションを保護できます。
- 高速なフィードバックループ – CIゲートがマージ前に破壊的な変更を停止させたことで、チームはコストのかかるロールバックを回避できました。
- コード品質の向上 – 契約テストを可能にするために、actuatorエンドポイントの追加やサインアップフローの冪等化が必要となりましたが、それが結果としてサービスの堅牢性を高めることにつながりました。
開発者が検討すべきトレードオフ
契約テストはメンテナンスのオーバーヘッドを増やします。仕様をコードと同期させ続ける必要があり、テスト生成プロセスによってビルド時間が長くなる可能性があります。特に手動テストで十分だと感じられる小規模なプロジェクトでは、追加される安全性にそれだけの労力をかける価値があるかどうかをチームで判断する必要があります。
今後の展望
- CIへの導入拡大 – より多くのチームが契約テストスイートをパイプラインに統合するにつれ、ツールはより高速で、より設定の柔軟性が高まっていくでしょう。
- 契約フォーマットの標準化 – 新たな仕様が登場することで、サービス間やチーム間での契約の共有が容易になる可能性があります。
- 仕様更新の自動化 – コードの変更から契約を推論するツールが登場すれば、手動でのメンテナンス負担が軽減されるかもしれません。
UIがスムーズに動いているからといって、APIが堅牢であると決めつけてはいけません。契約テストを実行すれば、手動チェックでは見逃してしまう隠れた欠陥を明らかにできます。CIパイプラインに実行可能な契約を追加することで、「問題なさそう」を「安全であると証明済み」に変えることができるのです。
