43

この質問は、以前に StackOverflow で尋ねられたこの質問のようなものだと思うかもしれません。しかし、私は物事を別の方法で見ようとしています。

TDD では、さまざまな条件、基準、検証コードを含むテストを記述します。クラスがこれらすべてのテストに合格すれば、準備完了です。これは、クラスが実際にやるべきことだけを確実に行う方法です。

Bertrand Meyersの著書Object Oriented Software Constructionを一言一句たどると、クラス自体に内部契約と外部契約があるため、本来の目的のみを実行し、それ以外は何も実行しません。契約が守られていることを確認するためのコードはクラスの一部であるため、外部テストは必要ありません。

物事を明確にするための簡単な例

TDD

  1. すべてのケースで値の範囲が (0 ~ 100) であることを確認するテストを作成します。

  2. テストに合格するメソッドを含むクラスを作成します。

DBC

  1. クラスを作成し、そのメンバーvarの範囲を (0-100) にするコントラクトを作成し、コントラクト違反のコントラクトを設定し、メソッドを定義します。

私は個人的にDBCアプローチが好きです。


純粋な DBC があまり普及していない理由はありますか? 言語やツール、アジャイルなのか、それともコードに責任を持つのが好きなのが私だけなのか?

私の考えが正しくないと思われる場合は、喜んで学びます。

4

9 に答える 9

35

DBC の主な問題は、ほとんどの場合、コントラクトを正式に指定できないか (少なくとも便利ではない)、現在の静的分析ツールでチェックできないことです。主流の言語 (Eiffel ではない) についてこの時点を過ぎるまで、DBC は人々が必要とする種類の保証を提供しません。

TDD では、(できれば) 十分に文書化されているメソッドの現在の自然テキスト仕様に基づいて、人間がテストを作成します。したがって、人間はテストを書くことによって正確さを解釈し、その解釈に基づいて何らかの保証を得る.

JavaDocs の作成に関する Sun のガイドを読むと、ドキュメントは基本的に、テスト計画を作成するのに十分な契約をレイアウトする必要があると書かれています。したがって、契約による設計は必ずしも TDD と相互に排他的ではありません。

于 2009-01-26T21:18:51.380 に答える
24

TDD と DbC は 2 つの異なる戦略です。DbC は実行時にフェイルファストを許可しますが、TDD は「コンパイル時」に機能します (正確には、コンパイルの直後に新しいステップを追加して単体テストを実行します)。

これは、DbC に対する TDD の大きな利点です。より早いフィードバックを得ることができます。TDD の方法でコードを記述すると、コードとその単体テストが同時に取得され、テストでエンコードした、本来あるべきだと思っていたとおりに「機能する」ことを確認できます。DbC を使用すると、テストが埋め込まれたコードを取得できますが、それでも実行する必要があります。IMO、これは確かに、Dbc があまり普及していない理由の 1 つです。

その他の利点 : TDD は、リグレッションの検出 (読み取り防止) を可能にし、リファクタリングを安全にする自動テスト スイートを作成するため、設計を段階的に拡張できます。DbC はこの可能性を提供しません。

現在、DbC を使用してフェールファストを行うことは、特にコードが他のコンポーネントとインターフェイスをとっている場合や、外部ソースに依存する必要がある場合に非常に役立ちます。この場合、コントラクトをテストすることで時間を節約できます。

于 2009-01-27T07:29:23.787 に答える
21

まず第一に、私はエッフェルのソフトウェアエンジニアなので、経験から問題を話すことができます.


TDD と DbCの前提が間違っている

2 つの技術は相反するものではなく、補完し合うものです。補足は、アサーションと目的の配置に関係しています。

TDD の目的には、コンポーネントとスコープの両方があります。TDD の基本コンポーネントは、ブール アサーションとオブジェクト機能 (メソッドなど) の実行です。手順は簡単です。

  1. オブジェクトを作成します。
  2. 機能でいくつかのコードを実行します。
  3. オブジェクトのデータの状態についてアサーションを行います。

失敗するアサーションは、テストに失敗します。すべてのアサーションを渡すことが目標です。

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 は単体テスト時に有効であることを指摘しました。コントラクトは、コンパイラの監視と制御の下でコードに適用されるため、コードを実行できるあらゆる場所に適用されます。

  1. ワークベンチ: プログラマーとして、コードを使用して動作を確認します (たとえば、TDD 時間の前に、または TDD 時間と組み合わせて)。

  2. 単体テスト: 継続的インテグレーション テスト、単体テスト、TDD など。

  3. アルファ テスト: 最初のテスト ユーザーは、実行可能ファイルを実行するときに契約につまずくでしょう。

  4. ベータ テスト: より多くのユーザーが契約につまずく可能性があります。

  5. 本番: 最終的な実行可能ファイル (または本番システム) は、コントラクトを通じて適用される継続的なテストを持つことができます (TDD はできません)。

上記の各状況では、どのコントラクトがどのソースから実行されるかを制御できることがわかります。さまざまな形式のコントラクトを選択的かつきめ細かくオン/オフし、コンパイラによって適用される場所とタイミングを非常に正確に制御できます。

そして、これだけでは不十分な場合、コントラクト (設計上) は、TDD アサーションでは決してできないことを行うことができます:呼び出しスタックのどこで、どのクライアントとサプライヤーの関係が壊れているのか、そしてその理由を教えてくれます(これは、どのように壊れているかをすぐに示唆します)それを修正します)。なぜこれが真実なのですか?

TDD アサーションは、事後にコード実行 (実行) の結果を通知するように設計されています。TDD アサーションは、検査中のメソッドの現在の状態までしか見ることができません。コードベースの外側の位置から TDD アサーションができないことは、失敗した呼び出しとその理由を正確に伝えることです! おわかりのように、あるメソッドへの最初の TDD 呼び出しがそのメソッドをトリガーします。多くの場合、そのメソッドは別のメソッドを呼び出し、別のメソッドを呼び出し、また別のメソッドを呼び出します。コール スタックが上下するにつれて、壊れることがあります。何かが間違ってデータを書き込むか、まったく書き込まないか、または書き込むすべきでないときにそれ。

TDD は、殺人事件が発生した後に犯罪現場に現れる警察のようなものです。彼らが残したのは、容疑者と有罪判決につながることを望んでいる法医学的手がかりだけです. しかし、犯罪が行われているときに私たちがそこにいることができたらどうでしょうか? これが、TDD アサーションとコントラクト アサーションの配置の違いです。コントラクトは進行中の犯罪を捕捉するために存在し、犯罪を犯している犯罪者を直接指し示します。

要約

要約しましょう。

  • TDD は DbC と矛盾していません。

  • これは、テクノロジーの補完的で協調的なセットですが、さまざまな機能と目的、およびそれらを操作するためのツールを備えています。

  • コントラクトはさらに到達し、コードが壊れたときにコードについてより多くを明らかにします。

  • TDD は、契約が実行されるための触媒の 1 つの形式です。

結論:両方欲しい!このすべてを読んだ後 (生き残った場合)、あなたもそうであることを願っています。

于 2015-02-23T18:27:54.133 に答える
12

契約による設計とテスト駆動開発は、相互に排他的ではありません

Bertrand Meyer の著書Object Oriented Software Construction, 2nd Editionでは、決して間違いを犯さないとは言っていません。実際、「コントラクトが破られたとき」の章を見ると、関数がコントラクトの状態を達成できなかった場合に何が起こるかが説明されています。

DbC 手法を使用するという単純な事実だけでは、コードは正しくなりません。コントラクトによる設計では、コントラクトの形式で、コードとそのユーザーに対して明確に定義されたルールを確立します。それは役に立ちますが、いつでも物事を台無しにすることができます.

テスト駆動型開発では、クラスのパブリック インターフェイスが正しく動作することをブラック ボックス スタイルの外からチェックします。

于 2009-01-26T21:20:04.560 に答える
8

どちらか一方だけでなく、両方の方法を組み合わせて使用​​ するのが最善だと思います。

クラスとそのメソッド自体の中でコントラクトを完全に強制することは、非現実的である可能性があると私には常に思えてきました。

たとえば、関数が何らかの方法で文字列をハッシュし、ハッシュされた文字列を出力として返すと言う場合、関数は文字列が正しくハッシュされたことをどのように強制しますか? もう一度ハッシュして、一致するかどうかを確認しますか? ばかげているようです。ハッシュを逆にして、オリジナルを取得するかどうかを確認しますか? ありえない。むしろ、関数が正しく動作することを確認するための一連のテスト ケースが必要です。

一方、特定の実装で入力データが特定のサイズである必要がある場合は、コントラクトを確立してコードに適用するのが最善の方法のようです。

于 2009-01-26T21:22:35.743 に答える
6

私の考えでは、TDD はより「誘導的」です。例 (テスト ケース) から始めて、コードはそれらの例に対する一般的なソリューションを具現化します。

DBC はより「演繹的」に見えます。要件を収集した後、オブジェクトの動作と契約を決定します。次に、それらのコントラクトの特定の実装をコーディングします。

動作の具体例であるテストよりも、コントラクトを書くことはやや難しいため、TDD が DBC よりも人気がある理由の 1 つかもしれません。

于 2009-01-26T21:14:34.213 に答える
5

私は過去に両方を使用しましたが、DBCスタイルは「邪魔にならない」ことがわかりました。DBCのドライバーは、実行中の通常のアプリケーションである可能性があります。ユニットテストの場合、いくつかの応答を期待(検証)するため、セットアップに注意する必要があります。DBCの場合、そうする必要はありません。ルールはデータに依存しない方法で記述されているため、セットアップやモックを行う必要はありません。

DBC / Pythonでの私の経験の詳細:http://blog.aplikacja.info/2012/04/classic-testing-vs-design-by-contract/

于 2012-09-18T21:00:40.847 に答える
5

両者が共存できない理由はわかりません。方法を見て、契約が何であるかを一目で正確に知ることは素晴らしいことです。また、単体テストを実行でき、最後の変更で何も壊れていないことを知っているのも素晴らしいことです。2つの手法は相互に排他的ではありません。なぜ契約によるデザインが人気がないのかは謎です。

于 2009-01-26T21:00:46.347 に答える
5

テスト駆動開発は特定の 1 つのケースを対象とするのに対し、Design By Contract はすべてのケースでの成功/失敗の仕様であると考えています。TDD ケースが成功した場合、関数はその仕事をしていると見なされますが、失敗する可能性のある他のケースは考慮されません。

一方、Design By Contract は、望ましい答えを保証する必要はなく、答えが「正しい」ことだけを保証します。たとえば、関数が null 以外の文字列を返すことになっている場合、ENSURE で想定できる唯一のことは、それが null ではないということです。

しかし、期待した文字列が返されない可能性があります。コントラクトがそれを判断できる方法はありません。仕様に従って実行していたことを示すことができるのはテストだけです。

したがって、この 2 つは補完的です。

グレッグ

于 2013-07-17T19:26:48.953 に答える