DbC と TDD には重複部分があると思いますが、作業が重複しているとは思いません。DbC を導入すると、おそらくテスト ケースの削減につながるでしょう。
説明させてください。
TDD では、テストは実際にはテストではありません。それらは動作仕様です。ただし、これらは設計ツールでもあります。最初にテストを作成することにより、テスト対象のオブジェクトの外部 API (実際にはまだ作成していません) をユーザーと同じ方法で使用します。そうすることで、実装しやすい方法ではなく、ユーザーにとって意味のある方法で API を設計できます。queue.full?
の代わりのようなものqueue.num_entries == queue.size
。
この 2 番目の部分を契約に置き換えることはできません。
最初の部分は、少なくとも単体テストでは、コントラクトに部分的に置き換えることができます。TDD テストは、他の開発者 (単体テスト) とドメイン エキスパート (受け入れテスト) の両方にとって、動作の仕様として機能します。コントラクトは、他の開発者やドメインの専門家だけでなく、コンパイラやランタイム ライブラリに対しても動作を指定します。
ただし、コントラクトには固定された粒度があります。メソッドの事前条件と事後条件、オブジェクトの不変条件、モジュール コントラクトなどがあります。ループバリアントと不変条件かもしれません。ただし、単体テストは動作の単位をテストします。それらはメソッドよりも小さいか、複数のメソッドで構成されている場合があります。それは契約でできることではありません。そして、「全体像」については、統合テスト、機能テスト、受け入れテストが必要です。
そして、DbC がカバーしていない TDD のもう 1 つの重要な部分があります。それは中央の D です。TDD では、テストが開発プロセスを駆動します。失敗したテストがない限り、実装コードを 1 行も書かないでください。すべてのテストに合格しない限り、テスト コードを最小限に抑える必要があります。テストに合格するための最小限の実装コードのみを記述し、失敗するテストを生成するための最小限のテスト コードのみを記述します。
結論として、テストを使用して API の「フロー」、「感触」を設計します。コントラクトを使用して、API のコントラクトを設計します。テストを使用して、開発プロセスの「リズム」を提供します。
このようなもの:
- 機能の受け入れテストを書く
- その機能の一部を実装するユニットの単体テストを書く
- 手順 2 で設計したメソッド シグネチャを使用して、メソッド プロトタイプを記述します。
- 事後条件を追加する
- 前提条件を追加
- メソッド本体を実装する
- 受け入れテストに合格した場合は 1 に進み、それ以外の場合は 2 に進みます
Design by Contract の発明者である Bertrand Meyer が TDD と DbC を組み合わせることについて考えていることを知りたい場合は、彼のグループによるContract-Driven Design = Test-Driven Development - Writing Test Casesという素晴らしい論文があります。基本的な前提は、コントラクトは考えられるすべてのケースの抽象的な表現を提供するのに対し、テスト ケースは特定のケースのみをテストするということです。したがって、コントラクトから適切なテスト ハーネスを自動的に生成できます。