テストを開始するためにデザインが必要な場合、TDDがどのように優れたデザインを取得するのに役立つかがわかりません。


50

TDD、特に開発の部分に頭を包み込もうとしています。私はいくつかの本を見てきましたが、私が見つけた本は主にテストの部分に取り組んでいます-NUnitの歴史、テストが良い理由、レッド/グリーン/リファクタリング、文字列計算機の作成方法。

良いものですが、それはTDDではなく「単なる」単体テストです。具体的には、テストを開始するためにデザインが必要な場合、TDDが優れたデザインを取得するのにどのように役立つか理解できません。

例として、次の3つの要件を想像してください。

  • カタログには製品のリストが必要です
  • カタログは、ユーザーが閲覧した製品を記憶する必要があります
  • ユーザーは製品を検索できる必要があります

この時点で、多くの本は魔法のうさぎを帽子から引き出して「ProductServiceのテスト」に飛び込みますが、そもそもProductServiceがあるという結論に至った経緯については説明していません。それが私が理解しようとしているTDDの「開発」の部分です。

既存の設計が必要ですが、エンティティサービス以外のもの(つまり、Productがあるため、ProductServiceが必要です)はどこにも見つかりません(たとえば、2番目の要件では、ユーザーですが、思い出させる機能をどこに配置しますか?また、検索はProductServiceの機能または別のSearchServiceの機能ですか?どちらを選択すべきかをどのように知ることができますか?)

SOLIDによると、UserServiceが必要になりますが、TDDなしでシステムを設計すると、単一メソッドサービスが大量に発生する可能性があります。TDDは、最初に自分のデザインを発見することを意図したものではありませんか?

私は.net開発者ですが、Javaリソースも機能します。実際のビジネスアプリケーションを扱う実際のサンプルアプリケーションや本はないようです。TDDを使用して設計を作成するプロセスを示す明確な例を提供できますか?


2
TDDは開発手法全体の一部にすぎません。もちろん、全体をまとめるには、何らかのデザイン(前もって、またはより進化したもの)を採用する必要があります。
陶酔

3
@gnat:TDDの書籍では設計プロセスが明確にならない理由の調査です。
ロバートハーヴェイ

4
@gnat:私の編集ではなく、あなたの編集でした。:)質問のタイトルと本文の私の変更を参照してください。
ロバートハーヴェイ

9
ロバートC.マーティンの作品を読んだり、彼のビデオの1つを見たりした場合、彼はしばしばデザインを念頭に置いているが、彼はそれに結婚していないことがわかります。彼、正しいデザインの先入観が彼のテストから現れると信じていますが、それを強制しません。そして、最終的には、そのデザインがそうなることもあれば、そうでないこともあります。ここでの私のポイントは、あなた自身の以前の経験があなたを導くということですが、テストはあなたを駆り立てるべきです。テストは設計を開発またはデバンクできる必要があります。
アンソニーペグラム

3
ですから、それは実際にテストすることではなく、設計することです。それだけでは、設計の検証を支援するのと同じくらい、設計を実際に支援するわけではありません。しかし、それは!@#$ ingテストではありませんか?
エリックReppen

回答:


17

TDDのアイデアは、テストから始めて、そこから作業することです。したがって、「カタログには製品のリストが必要」という例を挙げると、「カタログ内の製品の確認」のテストがあると見なされ、これが最初のテストになります。さて、カタログを保持するものは何ですか?製品を保持しているのは何ですか?これらは次の要素であり、アイデアは、最初のテストに合格することから生まれるProductServiceのようなものになるようないくつかの要素をまとめることです。

TDDの考え方は、テストから始めて、そのテストを最初のポイントとして合格させるコードを書くことです。ユニットテストはこの一部ですが、テストから始めてコードを書くことによって形成される全体像を見ていないので、まだコードがないので、この時点で盲点がありません。


スライド20〜22が重要なテスト駆動開発チュートリアル。そのアイデアは、結果として機能が何をすべきかを知り、そのためのテストを書き、ソリューションを構築することです。設計部分は、必要なものに応じて異なりますが、簡単な場合とそうでない場合があります。重要な点は、プロジェクトの後半に導入しようとするのではなく、最初からTDDを使用することです。最初にテストを開始する場合、これは役立つ可能性があり、ある意味で注意する価値があります。後でテストを追加しようとすると、延期または遅延される可能性があります。後のスライドも同様に役に立つかもしれません。


TDDの主な利点は、テストを開始することで、最初は設計に縛られないことです。したがって、アイデアはテストをビルドし、開発テストとしてこれらのテストに合格するコードを作成することです。事前に大きな設計を行うと、問題を引き起こす可能性があります。これにより、物事を所定の位置にロックして、構築中のシステムを最終的に軽快にするというアイデアが得られます。


Robert Harveyはこれをコメントに追加しました。これは答えで述べる価値があります。

残念ながら、これはTDDについての一般的な誤解だと思います 。ユニットテストを書いて合格させるだけでは、ソフトウェアアーキテクチャを成長させることはできません。単体テストの作成はデザインに影響しますが、デザインを作成するわけではありません。あなたはそれをしなければなりません。


31
@MichaelStum:残念ながら、これはTDDについてよくある誤解だと思います:単体テストを書いて合格させるだけではソフトウェアアーキテクチャを成長させることはできません。 単体テストの作成はデザインに影響しますが、デザインを作成するわけではありません。 あなたはそれをしなければなりません。
ロバートハーヴェイ

4
@ RobertHarvey、JimmyHoffa:あなたのコメントに100回投票することができたら、私はそうします!
ドックブラウン

9
@Robert Harvey:このよくある誤解について書いてくれてうれしいです。ただ座ってあらゆる種類の単体テストを書くだけで、デザインが自然に「出現」するということをよく耳にします。また、設計が悪い場合は、十分な単体テストを作成しなかったためです。テストは設計の要件を指定および検証するツールですが、設計を自分で「しなければならない」ことに同意します。全くもって同じ意見です。
ジョルジオ

4
@ Giorgo、RobertHarvey:私からRobertHarveyにも+1000。残念ながら、その誤解は十分に一般的であるため、一部の「専門家」TDD /アジャイル実践者はそれが真実であると信じています。たとえば、ドメインの知識や種類の分析なしに、TDDから数独ソルバーを「進化」させることができます。ロン・ジェフリーズがTDDの限界に関するフォローアップを発表したのか、それともなぜ結論や教訓を得ずに実験を突然止めたのかを説明したのだろうか。
アンドレスF.

3
@Andres F:数独の話は知っていますが、とても面白いと思います。一部の開発者は、ツール(TDDやSCRUMなど)がドメインの知識と自分の努力に取って代わることができると誤解していると思います。多くの場合、分析と設計に時間をかけすぎず、直接コードを作成することを好む人々です。彼らにとって、特定の方法論に従うことは、適切な設計を行わないためのアリバイです。しかし、これはTDOの誤用です。
ジョルジオ

8

何が価値があるために、TDDは私が最高のデザインに来ることができますより迅速に TDDをしていない以上。私はおそらく、それの有無にかかわらず、最高のデザインに来るでしょう。しかし、私がそれを考えてコードを数回突き刺すことに費やしていたはずの時間は、代わりにテストの作成に費やされます。そして、それは時間の短縮です。私のために。皆のためではありません。また、同じ時間がかかったとしても、一連のテストが残っているので、リファクタリングがより安全になり、さらに優れたコードにつながります。

それはどうしますか?

まず、すべてのクラスをクライアントコードへのサービスとして考えることをお勧めします。より良いコードは、コード自体がどのように見えるかを心配するのではなく、呼び出し元のコードがどのようにAPIを使用するかを考えることから生まれます。

第二に、私はそれを考えている間、1つの方法にあまりにも多くの循環的複雑さを書くのを止めます。メソッドを通る余分なパスごとに、必要なテストの数が2倍になる傾向があります。まったくの怠は、あまりにも多くのロジックを追加し、1つの条件を追加するために16個のテストを作成する必要があることを示しています。

本当に簡単です。それは魔法のデザインツールではありません。


6

私は頭をTDDに巻き付けようとしています...説明のために、次の3つの要件を想像してください。

  • カタログには製品のリストが必要です
  • カタログは、ユーザーが閲覧した製品を記憶する必要があります

これらの要件は、人間の言葉で言い換えるべきです。ユーザーが以前に表示した製品を知りたいのは誰ですか?ユーザー?セールスマン?

  • ユーザーは製品を検索できる必要があります

どうやって?名前で?ブランド別?テスト駆動開発の最初のステップは、たとえば次のようなテストを定義することです。

browse to http://ourcompany.com
enter "cookie" in the product search box
page should show "chocolate-chip cookies" and "oatmeal cookies"

>

この時点で、多くの本は魔法のうさぎを帽子から引き出して「ProductServiceのテスト」に飛び込みますが、そもそもProductServiceがあるという結論に至った経緯については説明していません。

これらが唯一の要件である場合、私は確かにProductServiceを作成することはありません。静的な製品リストを含む非常にシンプルなWebページを作成できます。製品を追加および削除する要件に達するまで、これは完全に機能します。その時点で、リレーショナルデータベースとORMを使用し、単一のテーブルにマップされたProductクラスを作成するのが最も簡単であると判断する場合があります。まだProductServiceはありません。ProductServiceなどのクラスは、必要なときに必要に応じて作成されます。同じクエリまたは更新を実行する必要がある複数のWeb要求がある場合があります。その後、コードの重複を防ぐためにProductServiceクラスが作成されます。

要約すると、TDDは記述されるコードを駆動します。実装を選択すると設計が行われ、コードをクラスにリファクタリングして重複を排除し、依存関係を制御します。コードを追加するとき、コードをSOLIDに保つために新しいクラスを作成する必要があります。ただし、ProductクラスとProductServiceクラスが必要になることを事前に決定する必要はありません。製品クラスだけで人生が完全にうまくいくことがわかるかもしれません。


OK、ノーProductService、その後。しかし、TDDは、データベースとORMが必要だとどのように伝えましたか?
ロバートハーヴェイ

4
@ロバート:そうではなかった。これは、要件を満たす最も効果的な方法の判断に基づいた設計上の決定です。しかし、決定は変わる可能性があります。
ケビンクライン

1
任意のプロセスの副作用として、良いデザインが生成されることはありません。システムまたはモデルを使用して作業を行うことは素晴らしいことですが、テスト優先のTDDであるIMOは、悪の副作用によって人々が予期せず噛まないことを保証するものとして自分自身を売ることによって利益相反にぶつかりますそもそも起こらないはずのコード。デザインには、熟考、認識、そして先見性が必要です。自動検出された症状をツリーから削除することからそれらを学習することはありません。そもそも邪悪なミュータントブランチを回避する方法を考え出すことでそれらを学びます。
エリックReppen

テストは製品を追加すると思います。コンピューターを再起動し、システムを再起動します。追加された製品はまだ表示されているはずです。」ある種のデータベースの必要性がどこから来たのかを示しています(ただし、フラットファイルまたはXMLでもかまいません)。
yatima2975

3

他の人は意見が異なるかもしれませんが、私にとって、新しい方法論の多くは、開発者が習慣や個人的な誇りから、古い方法論が綴ったことのほとんどを行うという前提に依存しています。作業はクリーンな言語またはやや厄介な言語のクリーナー部分にカプセル化されているため、すべてのテストビジネスを実行できます。

私が過去にこれに遭遇したいくつかの例:

  • 多数の仕様作業請負業者を引き取り、彼らのチームがアジャイルでテストファーストであることを伝えます。多くの場合、仕様どおりに作業する以外の習慣はなく、プロジェクトを完了するのに十分な長さである限り、作業の品質について心配することはありません。

  • 最初に何か新しいテストを試してみてください。さまざまなアプローチやインターフェースがくだらないことに気付いて、テストのリッピングに多くの時間を費やしてください。

  • 低レベルの何かをコーディングし、カバレッジの不足のために平手打ちされるか、関連付けられている基本的な動作をモックできないため、あまり価値のない多くのテストを記述します。

  • ディスクサブシステムやtcpipレベルの通信インターフェイスなど、基礎となるテスト不能なビットを最初に記述することなく、テスト可能な機能を追加するための基礎となるメカニズムが事前に十分に用意されていない状況。

TDDを実行していて、自分にとっては良いことですが、価値があるとは言えないもの(ジョブ全体、またはプロジェクトの段階)がたくさんあります。

あなたの例は、あなたがまだ設計にまだ慣れていないように聞こえるので、アーキテクチャの会話をする必要があるか、プロトタイプを作成しています。私の意見では、まずそのいくつかを乗り越える必要があります。


1

TDDは、システムの詳細設計(APIやオブジェクトモデルなど)にとって非常に貴重なアプローチであると確信しています。ただし、TDDの使用を開始するプロジェクトのポイントに到達するには、既に何らかの方法でモデル化された設計の全体像と、何らかの方法で既にモデル化されたアーキテクチャの全体像が必要です。@ user414076は、ロバートマーティンがデザインアイデアを念頭に置いているが、結婚していないと言い換えています。まさに。結論-TDDは進行中の唯一の設計活動ではなく、設計の詳細が具体化される方法です。TDDは、他の設計活動に先行し、全体的な設計の作成および進化の方法に対処する全体的なアプローチ(アジャイルなど)に適合する必要があります。

参考までに、具体的で現実的な例を与えるトピックについてお勧めする2冊の本:

テストに導かれたオブジェクト指向ソフトウェアの成長 -完全なプロジェクト例を説明し、提供します。これは設計に関する本であり、テストではありません。テストは、設計アクティビティ中に予想される動作を指定する手段として使用されます。

テスト駆動開発実践ガイド -小さいながらも完全なアプリの開発をゆっくりと段階的に説明します。


0

TTDは成功ではなくテストの失敗によって設計の発見を促進するため、未知の要素をテストし、最終的に単体テストの完全なハーネスにつながる未知の要素を繰り返し再テストすることができます。コードの作成/リリース後の改良。

たとえば、入力がいくつかの異なる形式であり、すべてがまだわかっているわけではないという要件があります。TDDを使用すると、まず入力形式が与えられたときに適切な出力が提供されることを確認するテストを作成します。明らかにこのテストは失敗するため、既知の形式を処理して再テストするコードを作成します。不明な形式は要件の収集を通じて公開されるため、コードが書き込まれる前に新しいテストが書き込まれ、これらも失敗するはずです。次に、新しい形式をサポートするために新しいコードが作成され、すべてのテストが再実行されて、回帰の可能性が低減されます。

ユニットの障害を「壊れた」コードではなく「未完成」のコードと考えることも役立ちます。TDDは未完成のユニット(予期しない障害)を許容しますが、破損したユニット(予期しない障害)の発生を減らします。


1
これは有効なワークフローであることに同意しますが、そのようなワークフローからどのように高レベルのアーキテクチャが生まれるかについては実際には説明していません。
ロバートハーヴェイ

1
確かに、MVCパターンのような高レベルアーキテクチャは、TDDだけでは実現しません。しかし、TDDから明らかになるのは、簡単にテストできるように設計されたコードです。これは、それ自体が設計上の考慮事項です。
ダニエルペレイラ

0

質問ではそれが述べられています:

...多くの本は魔法のうさぎを帽子から引き抜き、「ProductServiceのテスト」に飛び込みますが、そもそもProductServiceがあるという結論に至った経緯については説明していません。

彼らは、この製品をどのようにテストするかを考えることでその結論に達しました。「これはどのような製品ですか?」「まあ、サービスを作成できます」。「OK、そのようなサービスのテストを書きましょう」


0

機能には多くのデザインが含まれている可能性があり、TDDはどちらが最適かを完全には通知しません。さらに、テストがよりモジュール化されたコードの構築に役立つ場合でも、本番環境ではなくテスト要件に合ったモジュールを構築することができます。だから、あなたはどこに行くのか、全体像の中でどのように物事が収まるのかを理解しなければなりません。言い換えると、機能要件と非機能要件があります。最後の要件を忘れないでください。

設計に関しては、Robert C. Martinの書籍(アジャイル開発)を参照しますが、Martin FowlerのPatterns of Enterprise Application ArchitectureおよびDomain Driver Designも参照します。特に後者は、要件からエンティティとリレーションを抽出する際に非常に体系的です。

次に、これらのエンティティを管理する方法について利用可能なオプションについて良い気持ちが得られたら、TDDアプローチを提供できます。


0

TDDは、最初に自分のデザインを発見することを意図したものではありませんか?

番号。

最初に設計していないものをどのようにテストできますか?

例として、次の3つの要件を想像してください。

  • カタログには製品のリストが必要です
  • カタログは、ユーザーが閲覧した製品を記憶する必要があります
  • ユーザーは製品を検索できる必要があります

これらは要件ではなく、データの定義です。私はあなたのソフトウェアのビジネスが何であるかを知りませんが、アナリストがそのように話す可能性は低いです。

システムの不変条件を知る必要があります。

要件は次のようになります。

  • この製品の在庫が十分にある場合、顧客は特定の数量の製品を注文できます。

したがって、これが唯一の要件である場合は、次のようなクラスがあります。

public class Product {

  private int quantity;

  public Product(int initialQuantity) {
    this.quantity = initialQuantity;
  }

  public void order(int quantity) {
    // To be implemented.
  }

}

次に、TDDを使用して、order()メソッドを実装する前にテストケースを作成します。

public void ProductTest() {

    public void testCorrectOrder() {

        Product p = new Product(10);
        p.order(3);
        p.order(4);

    }

    @Expect(ProductOutOfStockException)
    public void testIncorrectOrder() {

        Product p = new Product(10);
        p.order(7);
        p.order(4);

    }

}

したがって、2番目のテストは失敗します。次に、好きな方法でorder()メソッドを実装できます。


0

TDDが与えられた設計の適切な実装をもたらすことは非常に正しいことです。設計プロセスには役立ちません。


ただし、作業コードを壊さずに設計を改善するためのセーフティネットを提供します。これは、ほとんどの人がスキップするリファクタリングです。
エイドリアンシュナイダー

-3

TDDは大いに役立ちますが、ソフトウェア開発には重要な部分があります。開発者は、作成中のコード聞く必要があります。リファクタリングはTDDサイクルの3番目の部分です。これは、開発者が次の赤いテストに進む前に集中して考えるべき主なステップです。重複はありますか?SOLID原則は適用されますか?高い凝集力と低い結合はどうですか?名前はどうですか?テストから出てきたコードを詳しく見て、変更、再設計が必要なものがあるかどうかを確認してください。コードとコードから、どのように設計したいかがわかります。 私は通常、複数のテストのセットを作成し、そのリストを調べて最初のシンプルなデザインを作成します。新しいテストを追加すると変更されるため、「最終」である必要はありません。それがデザインの出番です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.