TDDを使用しない単体テストの感覚


28

TDDを使用して開発する予定の新しい(非常に大きな)プロジェクトが開始されました。

TDDのアイデアは失敗しました(多くのビジネス上の理由とビジネス以外の理由)が、今は会話があります-いずれにしてもユニットテストを書くべきかどうか。私の友人は、TDDを使用せずに単体テストを書くことには意味がない(またはゼロに近い)ため、統合テストのみに焦点を合わせる必要があると言います。単純な単体テストを記述することには、コードの将来性をより高めるために、まだある程度の意味があると私は反対に思います。どう思いますか?

追加:これは>> this question <<の複製ではないと思います-UTとTDDの違いを理解しています。私の質問は違いではなく、TDDなしで単体テストを書く感覚です。


22
私は...、このような不条理なスタンスのためのあなたの友人をしている推論何好奇心が強い
Telastyn

11
いくつかの単体テストを含むプロジェクトの大部分はTDDを使用していないと思います。
ケーシー14

2
統合レベルはどうなりますか?あなたのユニットは何になりますか?各テストレベル以下でリファクタリングする頻度はどれくらいですか?統合テストの実行速度はどれくらいですか?彼らは書くのがどれくらい簡単ですか?コードのさまざまな部分で生成される組み合わせケースはいくつありますか?など...これらに対する答えがわからない場合、確固とした決定を下すには時期尚早かもしれません。時々TDDは素晴らしいです。利点はあまり明確ではない場合があります。ユニットテストが不可欠な場合があります。場合によっては、適切な統合テストのセットでほぼ同じくらいのコストがかかり、はるかに柔軟になります...オプションを開いたままにします。
トポ復活モニカ14

2
経験からの実践的なアドバイスとして、TDDを行っていない場合はすべてをテストないでください。どのような種類のテストが価値があるかを判断します。純粋な入出力メソッドでの単体テストは非常に価値があることがわかりましたが、アプリケーションの非常に高いレベルでの統合テスト(たとえば、Webアプリケーションで実際にWeb要求を送信する)も非常に貴重です。多くのモックのセットアップが必要な中間層統合テストと単体テストに注意してください。こちらのビデオもご覧ください:youtube.com/watch?v=R9FOchgTtLM
jpmc26 14

あなたが尋ねた質問に関して、あなたの更新は意味をなしません。TDDと単体テストの違いを理解しているなら、単体テストを書くことを妨げているものは何ですか。重複するのではなく、「あなたが何を求めているのかわからない」として閉じるための議論を見ることができましたが、あなたの質問を閉じたままにする投票。

回答:


52

TDDは主に(1)カバレッジを確保するため、(2)保守可能で理解しやすくテスト可能な設計を推進するために使用されます。TDDを使用しない場合、コードカバレッジが保証されません。しかし、それは決してその目標を放棄し、0%のカバー率で生き生きと生きるべきであることを意味します。

回帰テストは、理由のために考案されました。その理由は、長い目で見れば、書き込みに余分な労力を費やすよりも、防止されたエラーにより多くの時間を節約できるからです。これは何度も証明されています。そのため、あなたは真剣にはるかに優れたソフトウェアエンジニアリングの回帰テストをお勧めします(または、あなたが非常にすぐにそうそこにいることを下って行くことを計画している場合、すべての教祖よりも、組織がずっとあることを確信していない限りではありませんあなたは、はい、あなたのための長期的には)事実、世界中の他のすべての組織に当てはまる理由から、ユニットテストを絶対に行う必要があります。統合テストよりも早くエラーを検出し、それにより費用を節約できるからです。それらを書かないことは、通りに横たわっているだけで無料のお金を渡すようなものです。


12
「TDDを使用しない場合、コードカバレッジが保証されません。」:そうは思いません。2日間開発でき、次の2日間はテストを作成します。重要な点は、必要なコードカバレッジが得られるまで、機能が終了したとは見なさないことです。
ジョルジオ

5
@DougM - ...多分理想的な世界で
Telastyn

7
悲しいことに、TDDはモッキングと連動しており、人々がそれをやめるまで、テストがより速く実行されることしか証明されていません。 TDDは死んでいます。長時間のライブテスト。
MickyD 14

17
TDD はコードカバレッジを保証しません。 それは危険な仮定です。テストに対してコーディングし、それらのテストに合格することはできますが、それでもエッジケースがあります。
ロバートハーヴェイ14

4
@MickyDuncan私はあなたの懸念を完全に理解しているとは確信できません。モッキングは、あるコンポーネントを他のコンポーネントから分離するために使用される完全に有効な手法であるため、そのコンポーネントの動作のテストは独立して実行できます。はい、極端に考えれば、ソフトウェアの設計が過剰になる可能性がありますが、不適切に使用された場合、開発手法も同様です。それに、DHHがあなたが引用する記事で述べているように、完全なシステムテストのみを使用するという考えは、実際には悪くないにしても、同じくらい悪いです。 特定の機能をテストする最適な方法を判断するには、判断を使用することが重要です
ジュール14

21

私のために起こっていることから、関連する逸話があります。私はTDDを使用しないプロジェクトに取り組んでいます。私たちのQAの人々は私たちをその方向に動かしていますが、私たちは小さな衣装であり、長く引き抜かれたプロセスでした。

とにかく、私は最近、サードパーティのライブラリを使用して特定のタスクを実行していました。そのライブラリの使用に関して問題があったので、本質的に同じライブラリのバージョンを自分で書くことになりました。合計で、約5,000行の実行可能コードと約2か月の時間になりました。私はコードの行が貧弱なメトリックであることを知っていますが、この答えのために私はそれが規模のまともな指標だと感じています。

任意のビット数を追跡​​できるようにするために必要な特定のデータ構造が1つありました。プロジェクトはJavaであるため、Javaを選択BitSetして少し変更しました(先頭0のsも追跡する機能が必要でしたが、JavaのBitSetは何らかの理由でこれを行いません....)。〜93%のカバレッジに達した後、私が書いたシステムに実際にストレスをかけるテストをいくつか書き始めました。機能の特定の側面をベンチマークして、それらが最終要件に十分対応できるようにする必要がありました。当然のことながら、BitSetインターフェイスからオーバーライドした機能の1つは、大きなビットセット(この場合は数億ビット)を処理するとき、とてつもなく低速でした。他のオーバーライドされた関数はこの1つの関数に依存していたため、大きなボトルネックでした。

私がやったことは、描画ボードに行き、の基本的な構造を操作する方法を見つけ出すことでしBitSetlong[]。アルゴリズムを設計し、同僚に入力を求めてから、コードを書き始めました。次に、ユニットテストを実行しました。それらのいくつかは壊れており、それを修正するためにアルゴリズムを調べる必要がある場所を正確に指し示しました。単体テストからのすべてのエラーを修正した後、私は関数が本来どおりに機能すると言うことができました。少なくとも、この新しいアルゴリズムが以前のアルゴリズムと同様に機能することを確信できました。

もちろん、これは防弾ではありません。単体テストがチェックしていないバグが私のコードにある場合、私はそれを知りません。しかし、もちろん、そのまったく同じバグは、遅いアルゴリズムでも同様でした。 ただし、その特定の関数からの誤った出力を心配する必要はないと自信を持って言うことができます。既存の単体テストにより、新しいアルゴリズムが正しいことを確認するために何時間、おそらく何日かを節約できました。

それが、TDDに関係なく単体テストを行うポイントです。つまり、コードをリファクタリング/保守するときに、TDD内およびTDDの外部で単体テストが行​​われます。もちろん、これは通常の回帰テスト、煙テスト、ファジーテストなどと組み合わせる必要がありますが、ユニットテストは名前が示すように、可能な限り最小の原子レベルで物事をテストし、エラーが発生した場所の方向性を示します。

私の場合、既存の単体テストがなければ、アルゴリズムが常に機能することを保証する方法をどうにかして考えなければなりません。結局のところ、ユニットテストによく似ていると思いませんか?


7

コードを大きく4つのカテゴリに分類できます。

  1. シンプルでめったに変更されません。
  2. シンプルで頻繁に変更されます。
  3. 複雑で、めったに変更されません。
  4. 複雑で頻繁に変更されます。

単体テストは、リストのさらに下に行くほど重要になります(重要なバグをキャッチする可能性が高い)。私の個人的なプロジェクトでは、ほとんどの場合、カテゴリ4でTDDを実行します。カテゴリ3では、手動テストがより簡単で高速でない限り、通常はTDDを実行します。たとえば、アンチエイリアスコードは書くのが複雑ですが、ユニットテストを書くよりも視覚的に検証する方がはるかに簡単なので、コードが頻繁に変更される場合にのみユニットテストの価値があります。私のコードの残りの部分は、その機能のバグを見つけた後に単体テストを行いました。

特定のコードブロックがどのカテゴリに該当するかを事前に知ることは難しい場合があります。TDDの価値は、複雑な単体テストを誤って見逃さないことです。TDDのコストは、単純な単体テストの作成に費やすすべての時間です。ただし、通常、プロジェクトを経験した人は、コードのさまざまな部分がどのカテゴリに適合するかを合理的な程度の確実性で知っています。TDDを実行していない場合は、少なくとも最も価値のあるテストを作成してください。


1
アンチエイリアシングの例で提案したようなコードで作業する場合、コードを実験的に開発し、後でアルゴリズムを誤って壊さないようにするためにいくつかの特性テストを追加することが最善であることがわかります。特性評価テストは非常に迅速かつ簡単に開発できるため、これを行うオーバーヘッドは非常に低くなります。
ジュール14

1

単体テスト、コンポーネントテスト、統合テスト、受け入れテストのいずれであっても、重要な部分は自動化する必要があることです。自動テストを行わないことは、単純なCRUDから最も複雑な計算まで、あらゆる種類のソフトウェアにとって致命的な間違いです。その理由は、自動化されたテストを書くことは、そうしないときにすべてのテストを手動で実行する継続的な必要性よりも常にコストがかからないということです。それらを書いたら、ボタンを押すだけで合格か不合格かを確認できます。手動テストの実行には常に長い時間がかかり、テストに合格したか失敗したかを確認できるようにするために、人間(退屈したり、注意を失ったりする可能性のある生き物)に依存します。つまり、常に自動化されたテストを作成してください。

さて、同僚がTDDを使用せずにあらゆる種類の単体テストを行うことに反対する理由については、実稼働コードの後に​​書かれたテストを信頼するのが難しいためと思われます。また、自動化されたテストを信頼できない場合は、何の価値もありません。TDDサイクルに続いて、最初にテストを失敗させ(正当な理由により)、本番コードを記述してそれをパスする(そしてそれ以上)ようにする必要があります。このプロセスでは、基本的にテストをテストするため、テストを信頼できます。実際のコードの前にテストを書くと、ユニットとコンポーネントをより簡単にテストできるように設計することになります(高レベルのデカップリング、SRP適用など)。もちろん、TDDを行うには規律が必要です。

代わりに、すべての製品コードを最初に記述する場合、そのテストを記述するときに、最初の実行で合格することが期待されます。あなたは正しい動作をアサートせずにカバー本番コードの100%を、(でも任意のアサーションを行わなくてもよいが!ことをテスト作成している可能性があるため、これは非常に問題がある、私はこれが起こる見てきましたが)あなたはそれが失敗見ることができないので、まず、正しい理由で失敗しているかどうかを確認します。したがって、偽陽性になる可能性があります。偽陽性は最終的にテストスイートへの信頼を破壊し、本質的には手動テストに頼らざるを得ないため、両方のプロセス(テストの作成+手動テスト)のコストがかかります。

これは、TDDと同様に、テストテストする別の方法を見つける必要があることを意味します。そのため、テストを信頼できるようにするために、プロダクションコードの一部をデバッグしたりコメントしたりします。問題は、「テストをテストする」プロセスがこのようにはるかに遅いことです。私の経験では、アドホックテストを手動で実行する時間にこの時間を追加すると(実稼働コードをコーディングしている間は自動テストが行​​われないため)、全体的なプロセスが練習よりもはるかに遅くなりますTDD "by the book"(Kent Beck-例によるTDD)。また、私はここで賭けをし、それらが書かれた後に本当に「あなたのテストをテストする」ことはTDDよりもはるかに規律がかかると言います。

そのため、チームはTDDを行わない「ビジネス上の理由とビジネス以外の理由」を再考できるかもしれません。私の経験では、コードの実行後に単体テストを書くのに比べて、TDDは遅いと思う傾向があります。あなたが上で読んだように、この仮定には欠陥があります。


0

多くの場合、テストの品質は出所に依存します。私は定期的に「本物の」TDDを行わないことで罪を犯します-私が使用したい戦略が実際に機能することを証明するコードを書き、その後コードがテストでサポートすることを意図している各ケースをカバーします。通常、コードの単位はクラスであり、テストカバレッジなしで私が喜んでどのくらいの作業を行うか、つまり大量ではないという一般的なアイデアを提供します。これが意味するのは、テストのセマンティックな意味が「終了」状態のテスト対象システムとうまく一致するということです。なぜなら、SUTがどのようなケースを満たし、どのように満たすかを知っているからです。

逆に、積極的なリファクタリングのポリシーを持つTDDは、少なくともテストの変更対象システムのパブリックインターフェイスと同じくらい速くテストを廃止する傾向があります。私は個人的には、両方のアプリケーションの機能ユニットを設計する精神的なワークロードを見つける私の濃度と頻繁にはスリップテストのメンテナンスを維持するために高すぎると同期して、それをカバーするテストのセマンティクスを維持します。コードベースは、価値のあるものを何もテストしないか、単純に間違っているテストにぶらぶらしてしまいます。テストスイートを最新の状態に保つための規律と精神的な能力がある場合は、必ず、TDDを必要に応じて厳密に練習してください。私はそうしませんので、その理由であまり成功していません。


0

実際、ボブおじさんは彼のClean Codersのビデオの1つで非常に興味深い点に言及しました。彼は、Red-Green-Refactorサイクルは2つの方法で適用できると述べました。

1つ目は、従来のTDD方式です。失敗したテストを作成し、テストに合格して最終的にリファクタリングします。

2番目の方法は、非常に小さな量産コードを記述し、その直後に単体テストを実行してからリファクタリングすることです。

アイデアは非常に小さなステップで行くことです。もちろん、テストが赤から緑になったという実稼働コードからの検証は失われますが、TDDを理解しようとすることさえ拒否したジュニア開発者と私が主に仕事をしていた場合には、いくらか効果的であることが証明されました。

繰り返しますが(これはボブおじさんによって強調されました)、アイデアは非常に小さなステップで行って、追加されたばかりの製品コードをすぐにテストすることです。


「...アイデアは非常に小さなステップで進め、追加されたばかりの製品コードをすぐにテストすることです。」:私は同意しません。あなたが何をしたいのかについて明確なアイデアをすでに持っていて、詳細に取り組みたい場合、良いと説明するものは、最初に全体像を把握する必要があります。それ以外の場合、非常に小さなステップ(テスト、開発、テスト、開発)を行うことで、詳細に迷うことがあります。「行き先がわからない場合、そこに着かないかもしれません。」
ジョルジオ14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.