まず第一に、私はエッフェルのソフトウェアエンジニアなので、経験から問題を話すことができます.
TDD と DbCの前提が間違っている
2 つの技術は相反するものではなく、補完し合うものです。補足は、アサーションと目的の配置に関係しています。
TDD の目的には、コンポーネントとスコープの両方があります。TDD の基本コンポーネントは、ブール アサーションとオブジェクト機能 (メソッドなど) の実行です。手順は簡単です。
- オブジェクトを作成します。
- 機能でいくつかのコードを実行します。
- オブジェクトのデータの状態についてアサーションを行います。
失敗するアサーションは、テストに失敗します。すべてのアサーションを渡すことが目標です。
TDD と同様に、Design-by-Contract の契約には目的、範囲、およびコンポーネントがあります。TDD は単体テスト時間に制限されていますが、コントラクトは SDLC (ソフトウェア開発ライフサイクル) 全体を通して有効です! TDD の範囲内で、オブジェクト メソッド (機能) を実行すると、コントラクトが実行されます。Eiffel Studio Auto-test (TDD) セットアップでは、オブジェクトを作成し、呼び出しを行います (他の言語の TDD と同様)。
自動テスト付きの Eiffel Studio とコントラクト付きの Eiffel コードでは、目的が多少異なります。クライアントとサプライヤーの関係をテストしたいと考えています。私たちの TDD コードは、そのオブジェクトの Supplier メソッドの Client のふりをしています。オブジェクトを作成し、この目的に基づいてメソッドを呼び出します。単純な「TDD っぽいメソッド テスト」だけではありません。メソッド (機能) の呼び出しにはコントラクトがあるため、それらのコントラクトは自動テストで TDD 風のコードの一部として実行されます。これが正しいため、コードに配置するコントラクト アサーション (テスト) は、TDD テスト コードに表示する必要はありません。(プログラマーとしての) 私たちの仕事は、A) コード + コントラクトがすべての N パスに沿って実行されること、および B) コード + コントラクトがすべての適切なデータ型と範囲を使用して実行されることを確認することです。
TDD と DbC の補完関係については、おそらくもっと書きたいことがありますが、この件に関しては、あなたに無礼を言うつもりはありません。TDD と DbC は他のものと対立していません。
TDD の到達範囲を超えた DbC のコントラクトの力
ここで、TDD が到達できる範囲を超えた Design-by-Contract のコントラクトの力に注意を向けることができます。
コントラクトはコード内に存在します。それらは外部ではなく、内部にあります。コントラクトに関する最も強力な部分 (クライアントとサプライヤーのコントラクト関係の基礎を超えて) は、コンパイラーがコントラクトについて知るように設計されていることです! それらはエッフェルへのボルトオン追加ではありません! したがって、それらは継承のあらゆる側面に参加します (従来の垂直ジェネリックと水平ジェネリックの両方)。さらに、TDD が到達できない場所、つまりメソッド (機能) 内に到達します。
TDD は事前条件と事後条件をある程度簡単に模倣できますが、TDD はコード内に到達してループ不変コントラクトを実行することはできません。また、実行中のコード ブロックに沿って定期的なスポット チェック「チェック」コントラクトを実行することもできません。これは強力な論理的かつ定性的なパラダイムであり、契約による設計がどのように機能するかについての現実です。
さらに、TDD はクラス不変条件を行うことはできませんが、ごくわずかな方法で行うことができます。クラス不変の模倣を行うために、自動テスト コード (実際には適用された TDD の Eiffel Studio バージョンにすぎません) を取得するために最善を尽くしました。それは不可能。Eiffel クラスの不変量がどのように機能するかを詳しく知る必要がある理由を理解するには。したがって、現時点では、TDD にはこのタスクが不可能であり、DbC は非常に簡単に、うまく、エレガントに処理できるという私の言葉を信じる (または信じない) 必要があります。
DbCの到達範囲は上記の概念にとどまりません
上記で、TDD は単体テスト時に有効であることを指摘しました。コントラクトは、コンパイラの監視と制御の下でコードに適用されるため、コードを実行できるあらゆる場所に適用されます。
ワークベンチ: プログラマーとして、コードを使用して動作を確認します (たとえば、TDD 時間の前に、または TDD 時間と組み合わせて)。
単体テスト: 継続的インテグレーション テスト、単体テスト、TDD など。
アルファ テスト: 最初のテスト ユーザーは、実行可能ファイルを実行するときに契約につまずくでしょう。
ベータ テスト: より多くのユーザーが契約につまずく可能性があります。
本番: 最終的な実行可能ファイル (または本番システム) は、コントラクトを通じて適用される継続的なテストを持つことができます (TDD はできません)。
上記の各状況では、どのコントラクトがどのソースから実行されるかを制御できることがわかります。さまざまな形式のコントラクトを選択的かつきめ細かくオン/オフし、コンパイラによって適用される場所とタイミングを非常に正確に制御できます。
そして、これだけでは不十分な場合、コントラクト (設計上) は、TDD アサーションでは決してできないことを行うことができます:呼び出しスタックのどこで、どのクライアントとサプライヤーの関係が壊れているのか、そしてその理由を教えてくれます(これは、どのように壊れているかをすぐに示唆します)それを修正します)。なぜこれが真実なのですか?
TDD アサーションは、事後にコード実行 (実行) の結果を通知するように設計されています。TDD アサーションは、検査中のメソッドの現在の状態までしか見ることができません。コードベースの外側の位置から TDD アサーションができないことは、失敗した呼び出しとその理由を正確に伝えることです! おわかりのように、あるメソッドへの最初の TDD 呼び出しがそのメソッドをトリガーします。多くの場合、そのメソッドは別のメソッドを呼び出し、別のメソッドを呼び出し、また別のメソッドを呼び出します。コール スタックが上下するにつれて、壊れることがあります。何かが間違ってデータを書き込むか、まったく書き込まないか、または書き込むすべきでないときにそれ。
TDD は、殺人事件が発生した後に犯罪現場に現れる警察のようなものです。彼らが残したのは、容疑者と有罪判決につながることを望んでいる法医学的手がかりだけです. しかし、犯罪が行われているときに私たちがそこにいることができたらどうでしょうか? これが、TDD アサーションとコントラクト アサーションの配置の違いです。コントラクトは進行中の犯罪を捕捉するために存在し、犯罪を犯している犯罪者を直接指し示します。
要約
要約しましょう。
TDD は DbC と矛盾していません。
これは、テクノロジーの補完的で協調的なセットですが、さまざまな機能と目的、およびそれらを操作するためのツールを備えています。
コントラクトはさらに到達し、コードが壊れたときにコードについてより多くを明らかにします。
TDD は、契約が実行されるための触媒の 1 つの形式です。
結論:両方欲しい!このすべてを読んだ後 (生き残った場合)、あなたもそうであることを願っています。