TDD:私はそれを正しくやっていますか?


14

私は新しいプログラマーです(約1年しか学んでいません)。それをより良くするという目標で、最近TDDについて学びました。非常に役立つと思われるため、私はそれを使用する習慣を身に付けたかったのです。正しく使用していることを確認して確認したかったのです。

私がやっていること:

  1. 私が必要とする新しい方法を考えてください。
  2. そのメソッドのテストを作成します。
  3. テストに失敗します。
  4. 書き込み方法。
  5. テストに合格。
  6. メソッドをリファクタリングします。
  7. 繰り返す。

私が書いたすべての方法でこれをやっていますが、気にするべきではないものがありますか?後で、私は通常、既存のメソッドを別の方法または状況でテストする方法を考えます。私が考えているこれらの新しいテストを作成する必要がありますか、または各メソッドがすでに独自のテストを持っているので、気にする必要はありませんか?コードをテストしすぎることはできますか?

編集

また、これは私がただ疑問に思っていたものでした。GUIの作成などを行う場合、そのような状況ではTDDが必要ですか?個人的には、そのためのテストをどのように書くか考えられません。


5
あなたはすでにすべてをテストしていると言うベテランの専門家よりもずっと上手くやっています(しかしそうではありません)。
ヤニス

あなたが説明するのは、TDD の精神ではありません。

1
ATDDまたはBDDを調べてください。
dietbuddha

おそらくもっと高いところから始めましょう- 必要な新しいモジュールを考えてください。

回答:


16

あなたがワークフローとして説明していることは、私の考えではTDD の精神ではありません。

AmazonのKent Becks本のあらすじはこう言っています:

簡単に言うと、テスト駆動開発は、アプリケーション開発の不安を取り除くことを目的としています。一部の恐怖は健全ですが(多くの場合、プログラマーに「注意!」する良心と見なされます)、著者は、恐怖の副産物には建設的な批判を吸収できない暫定的、不機嫌、非コミュニケーションプログラマーが含まれると考えています。プログラミングチームがTDDを購入すると、すぐに良い結果が得られます。彼らは仕事に伴う恐れを取り除き、彼らに直面する困難な課題に取り組むためのより良い設備を備えています。TDDは暫定的な特性を排除し、プログラマーにコミュニケーションを促し、チームメンバーに批判を求めるように促します。要するに、TDDの背後にある前提は、コードを継続的にテストし、リファクタリングする必要があるということです。

実用的なTDD

正式な自動テスト、特にすべてのクラスのすべてのメソッドの単体テストは、アンチパターンと同じくらい悪いものであり、何もテストしていません。バランスを取る必要があります。すべてのsetXXX/getXXXメソッドの単体テストを書いていますか、それらもメソッドです!

また、テストは時間とお金の節約に役立ちますが、開発に時間とお金がかかり、コードであるため、メンテナンスに時間とお金がかかることを忘れないでください。メンテナンス不足から萎縮すると、利益よりも負債になります。

このようなすべてのものと同様に、自分以外の誰にも定義できないバランスがあります。いずれのドグマも、おそらく正しいというより間違っています。

適切なメトリックは、ビジネスロジックにとって重要であり、要件の変更に基づいて頻繁に変更されるコードです。これらのことには、自動化された正式なテストが必要です。これは大きな投資回収率になります。

あなたもこの方法で働く多くの専門店を見つけるのに非常に苦労するでしょう。単純な煙テストが実施された後、すべての実用的な目的のために変化しないものをテストするためにお金を費やすことは、ビジネスに意味がありません。.getXXX/.setXXXメソッドの正式な自動化された単体テストを作成することは、この完全な時間の無駄の代表例です。

プログラムのテストがバグの存在を説得力を持って実証するかもしれないが、バグがないことを実証することはできないと指摘されてから20年になります。このよく知られた発言を熱心に引用した後、ソフトウェアエンジニアはその日の順序に戻り、クリソコスミックな精製を改良し続けた昔の錬金術師のように、テスト戦略を改良し続けます。

- エドガーW.ジクストラ。(1988年に書かれたので、今では45年近くになります。)

この回答も参照してください。


1
それは私が心配していたことのほとんどに対応しています。私のようにすべての方法をテストするべきではないと感じていましたが、確信はありませんでした。TDDについてさらに読む必要があるようです。
cgasser

@kevinclineほとんどの時間setXXX/getXXXはまったく必要ありません:)
チップ

1
その些細なgetXXXをメモして間違えたり、getXXXに遅延読み込みを導入して間違えたりすると、実際にゲッターをテストしたいことがあることがわかります。
フランクシェラー

13

あなたはとても近いです。このわずかに異なる方法で考えてみてください。

  1. 必要な新しい動作を考えてください。
  2. その動作のテストを作成します。
  3. テストに失敗します。
  4. 新しいメソッドを作成するか、既存のメソッドを拡張します。
  5. テストに合格。
  6. コードをリファクタリングします。
  7. 繰り返す。

すべてのプロパティに対してゲッターとセッターを自動的に作成しないでくださいメソッド全体を考えずに、すべての機能をカバーするテストを作成します。クラス内にプロパティをカプセル化し、必要な動作を提供するメソッドを記述してください。メソッドを事前に計画するのではなく、優れた設計に進化させます。TDDは設計プロセスであり、テストプロセスではないことに注意してください。他の設計プロセスよりも優れている点は、ビンに入れる紙切れではなく、自動化された回帰テストのストリームを残していることです。

また、ボブおじさんのTDDの3つのルールを思い出してください。

  1. 失敗した単体テストに合格しない限り、実稼働コードを作成することはできません。
  2. 失敗するのに十分な数以上の単体テストを作成することはできません。コンパイルの失敗は失敗です。
  3. 1つの失敗した単体テストに合格するのに十分な量を超える本番コードを記述することはできません。

1
@Zexanima:あなたは、私たちのほとんどが1年後よりもずっとうまくやっている。次のステップに誘導しようとしています。
-pdr

2
あなたがリンクしているこれらの3つのルールだと思います。誰にでも出会うすべてのプロダクションショップの99%で、非常に教訓的で非現実的で非常に硬直的です。

1
@FrankShearar またはそれは、原理主義の過激主義者の大胆な非現実的な発言とみなされ、大規模なものは無視されます。私はこの独断的な態度を持った店で働いてきましたが、彼らは教義を文字通り受け止め、その点を見落としました。実際のコードを実際にテストしないテストを作成し、モックおよび依存性注入フレームワークの機能をテストするだけで、何が重要かを混乱させます。

1
@pdr何かのスピリットは、その事の独断的な形式化された正規化に正反対です。哲学を持つこととそれを宗教にねじ込むことは別のことです。TDDは、独断的な独断的な宗教用語で議論されていないよりも多い。3つのルールは、そののプレゼンテーションで独断的および宗教と聞いますがあるテスト、テスト、テストの OPのような人にマントラ、彼らはそれらを取る文字通り、それが良いよりも害の原因となります。私はフランクに反論しました。二極化した発言は、善よりも大義に害を及ぼす可能性があります。

2
私のポイントは、教義は盲目的に何かを福音として受け入れることから来るということでした。分極化した声明を取り、それを試して、それがあなたをあなたの快適ゾーンから追い出させます。3-point-all-or-nothingの極端なアプローチを試さないと、データがないため、TDDに関連するトレードオフを評価できません。
フランクシェラー

5

他の応答に追加するものはほとんどありません。

  1. オーバーテストなどがあります。ユニットテストができるだけ重複しないようにします。同じコード内で同じ条件を複数のテストで検証しても意味がありません。一方、本番コードをリファクタリングし、そのセクションと重複する多くのテストがある場合、戻ってそれらのすべてのテストを修正する必要があります。一方、それらが重ならない場合、1つの変更はせいぜい1つのテストのみを中断します。

  2. あなたがテストを書くためのより良い方法を考えたからといって、私はそこに戻って書き直しを始めません。これは、同じクラス/関数を完璧にしようとするので、同じクラス/関数を書き続けて書き換える個人に戻っています。完璧になることはありませんので、続けてください。より良い方法を発見したら、それを心の奥に置いてください(またはテストのコメントに追加してください)。次にあなたがそこにいて、あなたが新しい方法に切り替えることの即時の利益を見るとき、それはリファクタリングの時です。それ以外の場合、機能が完了し、先に進んですべてが機能する場合は、機能したままにします。

  3. TDDは、すべての機能がテスト可能であることを確認するだけでなく、即座に価値を提供することに重点を置いています。機能を追加するときは、「クライアントに何が必要か」を尋ねることから始めます。次に、クライアントに必要なものを提供するインターフェースを定義します。次に、テストに合格するために必要なものをすべて実装します。TDDは、単純にパブリック関数をコーディングして各関数をテストするのではなく、ユースケースシナリオ(すべての「what-if」を含む)をテストすることにほとんど似ています。

  4. GUIコードのテストについて尋ねました。「Humble Dialog」および「MVVM」パターンを検索します。これらの両方の背後にある考え方は、実際にUI固有のロジックを持たない「ビューモデル」クラスのセットを作成することです。ただし、これらのクラスには、通常UIの一部であるすべてのビジネスロジックが含まれ、これらのクラスは100%テスト可能である必要があります。残っているのは非常に薄いUIシェルです。通常、そのシェルはテストカバレッジなしで残されますが、その時点ではほとんどロジックはありません。

  5. 既存のコードの大部分を持っている場合、他の人がほとんど示唆していないように、どこにでも単体テストを絶対に追加しないでください。それはあなたを永遠に連れ去り、ユニットテストを安定したクラスの80%に追加してもメリットがありません。クラスは近い将来(または近い将来)変更されません。ただし、新しい作業では、すべてのコードでTDD開発を使用すると非常に有益です。完了すると、自動化されたテストを備えたスイートになるだけでなく、実際の開発には大きな利点があります。

    • テスト容易性を考慮することにより、結合度が低くモジュール化されたコードを作成できます。
    • 公共の契約を他の何よりも先に検討することで、よりクリーンな公開インターフェースになります
    • コードを記述しているときに、アプリケーション全体を実行して正しいパスを強制的に実行しようとする場合と比較して、新しい機能の検証には数ミリ秒かかります。私のチームは、発生する適切な条件を取得できなかったという理由だけで、一度も実行されていないエラー処理コードをリリースしています。後でQAでこれらの条件が発生したときにどれだけ時間を浪費するかは驚くべきことです。そして、ええ、このコードの多くは、誰かが「スモークテストが完了したら、将来の大きな変更のための領域ではない」と考えていたものです。

1

テストされていないメソッド、つまりテストがいくつかあります。ただし、境界条件や他の値など、初期コードの作成後に追加される一部のテストについては、単一のメソッドで複数のテストが行​​われるようにするための注意事項があります。

コードをオーバーテストすることはできますが、通常は、誰かが入力のあらゆる順列をテストしたい場合に発生します。たとえば、文字を受け取るメソッドがある場合、入力可能なすべての値のテストを作成しますか?それがあなたがオーバーテストをするところです、IMO。


ああ、大丈夫。それは私がやっていることではありません。私は通常、最初のテストをすでに行った後で、メソッドを最終的にテストできる別の状況を考えてしまいます。それらの「余分な」テストを作成する価値があるか、それがやり過ぎかどうかを確認していました。
cgasser

十分に小さな増分で作業する場合、通常、テストが実際に機能することを合理的に確信できます。言い換えれば、(正しい理由で)テストが失敗するということは、それ自体でテストをテストすることです。しかし、そのレベルの「合理的に確実」は、テスト対象のコードほど高くはありません。
フランクシェラー

1

通常、あなたはそれを正しくやっています。

テストはコードです。したがって、テストを改善できる場合は、先に進み、リファクタリングします。テストを改善できると思う場合は、先に進んで変更してください。テストをより良いものに置き換えることを恐れないでください。

コードをテストする際には、コードが実行することをコードがどのように行うかを指定することをお勧めします。テストではメソッドの結果を確認する必要があります。これはリファクタリングに役立ちます。一部のメソッドは、他のテストの結果を検証するために使用するため、明示的にテストする必要はありません(単純なゲッターとセッター)。


ゲッターとセッターのテストも書いていたので、そのヒントをありがとう。これにより、不要な作業を節約できます。
cgasser

「一部のメソッドは明示的にテストする必要はありません(つまり、単純なゲッターとセッター)」-ゲッターとセッターをコピー/貼り付けして、その背後のフィールド名を変更するのを忘れたことはありませんか?単純なコードについてのことは、単純なテストが必要なことです-あなたは本当にどれくらいの時間を節約していますか?
-pdr

メソッドがテストされていないという意味ではありません。他のメソッドが設定されていることを確認するか、テストの実際のセットアップ中にチェックされます。ゲッターまたはセッターが正しく機能しない場合、プロパティが正しく設定されなかったため、テストは失敗します。それらは暗黙のうちに無料でテストされます。
シュライス

ゲッターとセッターのテストには時間がかからないので、おそらく引き続きテストを行います。ただし、コードをコピーして貼り付けることはないため、この問題は発生しません。
cgasser

0

TDDに対する私の意見は、このツールが「ポイントアンドクリック」スタイルの開発者の世界を作り出したということです。ツールが各メソッドのテストスタブを作成するからといって、すべてのメソッドのテストを記述する必要があるわけではありません。一部の人々は、TDDをBDD(ビヘイビア駆動開発)に「リネーム」しています。このテストでは、テストがはるかに大規模で、面倒な小さなメソッドではなく、クラスの動作をテストすることを目的としています。

使用するクラスをテストするようにテストを設計する場合、特に各メソッドの相互作用のテストを開始するときに、各メソッドよりも少し多く実行するテストの作成を開始すると、いくつかの利点が得られ始めます。メソッド。メソッドではなく、クラスのテストを書くと考えることができると思います。いずれにせよ、メソッドを組み合わせて使用​​する際に矛盾や矛盾がないことを確認するために、メソッドの組み合わせを実行する「受け入れテスト」を作成する必要があります。

TDDをテストと混同しないでください-そうではありません。TDDは、メソッドをテストするのではなく、要件を実行するコードを記述するように設計されています。それは、すべてのメソッドのテストコードを盲目的に書く人にとってしばしば失われる、微妙だが重要なポイントです。それは、あなたが書いたコードが本来のように機能することではなく、あなたのコードがあなたがやりたいことを確実に行うテストを書くべきであるということです。

BDD v TDDに関する適切なリンクがいくつかあります。それらをチェックしてください。


0

TDDを学習し始めたら、はい、失敗したテストパスを行う以外はコードを1行も書かず、失敗する(そして正しい/予想される理由で失敗する)だけのテストを書くという独断的なアプローチに従うべきです。

TDDが何であるかを学習したら、特定の種類のものはテストする価値がないと判断できます。これはあなたがすべてについて従うべき同じアプローチであり、日本の武道はこれを「shuhari」と呼びます。(リンクは、教師なしで学習の段階をどのように進めることができるかについても説明しています。これは、ほとんどの人がどのように学習しなければならないかです。)


0

私はあなたが過大評価していると信じています。

私は長年TDDを実践していますが、私の経験では、TDDが効果的に実行されると、主に2つの利点が得られます。

  • 迅速なフィードバックを提供する
  • リファクタリングを有効にする

迅速なフィードバックを提供する

特に動的言語では、関連するテストを1秒未満で実行できます。また、ソースファイルがディスク上で変更されると、これらのテストを自動的に実行するファイルシステムウォッチャーがあります。したがって、テストの待ち時間はほとんどなく、書いたコードが期待どおりに動作したかどうかがすぐにわかります。したがって、TDDは非常に効率的な作業方法につながります。

リファクタリングを有効にする

優れたテストスイートがある場合は、システムの設計方法に関する新しい洞察を得ることができるため、安全にリファクタリングできます。

優れたテストスイートを使用すると、コード内で責任を移動できますが、移動後もコードが期待どおりに機能するという自信があります。そして、テストコードを少し変更するだけでこれを実行できるはずです。

システムのすべてのメソッドのテストを作成する場合、コードを簡単にリファクタリングできないという可能性があります。コードのすべてのリファクタリングでは、テストコードに大幅な変更が必要になります。また、テストコードが期待どおりに動作することを確認できますか?または、誤ってテストコードにバグを導入しましたが、その結果、実稼働コードにバグが発生しましたか?

ただし、pdrの回答でも示唆されているように、テストを記述するときにメソッドではなく動作に焦点を当てると、システムをリファクタリングするときに必要な変更がはるかに少ないテストになります。

または、このプレゼンテーションでIan Cooperが言っているように(私は記憶から引用したので、正しく引用されないかもしれません):

新しいテストを書く理由は、新しいクラスを追加するのではなく、新しい動作を追加することです


-2

すべてのパブリックメソッドをテストする必要があります。

ここでのキャッチは、パブリックメソッドが非常に小さい場合、おそらくあまりにも多くの情報を公開していることです。すべてのプロパティをgetXXX()実際に公開する一般的な方法は、カプセル化を実際に破ります。

パブリックメソッドが実際にクラスの動作である場合は、テストする必要があります。そうでない場合、それらは適切なパブリックメソッドではありません。

編集: pdrの答えは私のものよりもはるかに完全です。

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