web-dev-qa-db-ja.com

単体テストをしないほうがよいのはいつですか?

私は小さな会社でソロ開発者として働いています。実際、私は会社で唯一の開発者です。私はいくつかの(比較的)大規模なプロジェクトを定期的に作成して維持していますが、それらをサポートするためのテストはありません。新しいプロジェクトを始めるとき、TDDアプローチを試すべきかどうかよく疑問に思います。それは良い考えのように思えますが、正直に言って、関連する余分な作業を正当化することはできません。

私は自分のデザインを前向きに考えるために一生懸命働いています。確かにある日、別の開発者が私のコードを保守するか、少なくともそれをトラブルシューティングする必要があることを理解しています。私は物事をできるだけ単純にし、把握するのが難しいことをコメントして文書化します。そして、実際のところ、これらのプロジェクトはそれほど大きくも複雑でもないので、まともな開発者はそれらを理解するのに苦労します。

私が見たテストの例の多くは、コードのすべての側面をカバーする特徴点にまで及びます。私は唯一の開発者であり、プロジェクト全体のコードに非常に近いので、書き込み後、手動でテストするパターンに従う方がはるかに効率的です。また、要件と機能は頻繁に変更されるため、テストを維持するとプロジェクトにかなりの抵抗が発生します。そうでなければビジネスニーズの解決に費やすことができる時間。

だから私は毎回同じ結論に終わります。投資収益率が低すぎます。

入社日に基づいて誰かが会社にいる年数を計算するなど、アルゴリズムを正しく記述したことを確認するために、いくつかのテストを時々セットアップしました。しかし、コードカバレッジの観点からは、コードの約1%をカバーしました。

私の状況では、ユニットテストを定期的に実施する方法をまだ見つけることができますか、それともそのオーバーヘッドを回避することで正当化できますか?

PDATE:省略した私の状況に関するいくつかのこと:私のプロジェクトはすべてWebアプリケーションです。すべてのコードをカバーするには、自動化されたUIテストを使用する必要がありますが、これは手動テストに比べて大きなメリットがまだ見られない領域です。

143
Ken Pespisa

私が見たテストの例の多くは、コードのすべての側面をカバーする特徴点にまで及びます。

そう? すべてをテストする必要はありません。関連するものだけ。

私は唯一の開発者であり、プロジェクト全体のコードに非常に近いので、書き込み後、手動でテストするパターンに従う方がはるかに効率的です。

それは実際には誤りです。それはより効率的ではありません。それは本当にただの習慣です。

他のソロ開発者が行うことは、スケッチまたはアウトラインを記述し、テストケースを記述してから、アウトラインに最終的なコードを入力します。

これは非常に効率的です。

また、要件と機能は頻繁に変更されるため、テストを維持するとプロジェクトにかなりの抵抗が発生します。

それもまた誤りです。テストはドラッグではありません。要件の変更はドラッグです。

要件を反映するようにテストを修正する必要があります。彼らの特徴点、または高レベルかどうか;最初または最後に書かれた。

テストに合格するまで、コードは完成しません。それがソフトウェアの唯一の普遍的な真実です。

「ここだ」という限られた受け入れテストを受けることができます。

または、いくつかの単体テストを行うことができます。

または、両方を持つことができます。

しかし、あなたが何をしても、ソフトウェアが機能することを実証するためのテストが常にあります。

少し形式的で、Niceユニットテストツールスイートを使用すると、そのテストがより便利になると思います。

85
S.Lott

瞬目で実行でき、緑または赤のライトが点灯する一連のテストがあるとします。このテストスイートがテストされたと想像してくださいすべて!テストスイートを実行するために^ Tを入力するだけでよいと想像してください。これはあなたにどのような力を与えますか?

何かを壊すことを恐れずにコードを変更できますか?古い機能を壊すことを恐れずに新しい機能を追加できますか?破損を恐れずに、乱雑なコードをすばやくクリーンアップできますか?

はい、あなたはそれらすべてを行うことができます!そして、時間の経過とともにあなたのコードはどうなりますか?それを掃除するリスクがないので、それはますますきれいになります。

肩に小さな妖精がいたとしましょう。コード行を作成するたびに、妖精はそのコード行が意図したとおりに機能することをテストするテストスイートに何かを追加します。したがって、数秒ごとに^ Tを押して、記述した最後のコード行が機能することを確認できます。

どの程度のデバッグを行うと思いますか?

これがファンタジーのように聞こえるなら、あなたは正しいです。しかし、現実はそれほど変わらない。まばたきを数秒に、妖精をTDDの規律に置き換えると、ほぼ完了です。

1年前に作成したシステムに戻ってきて、中心的なオブジェクトの1つを作成する方法を忘れたとします。作成できるすべての方法でそのオブジェクトを作成するテストがあります。あなたはそれらのテストを読んで、あなたの記憶をジョギングすることができます。 APIを呼び出す必要がありますか?そのAPIを呼び出すことができるあらゆる方法でそのAPIを呼び出すテストがあります。これらのテストは少しドキュメントであり、あなたが理解できる言語で書かれています。それらは完全に明確です。彼らはとても正式なので、彼らは実行します。そして、彼らはアプリケーションと同期できなくなります!

投資する価値はありませんか?冗談でしょ!どうして誰もその一連のテストを望まないのでしょうか?自分に好意を持ち、愚かさを馬鹿にするのをやめなさい。 TDDを上手に実行する方法を学び、どれだけ速く実行できるか、そしてコードがどれほどきれいであるかを見てください。

113
Uncle Bob.

あなたがしている間違いは、あなたがテストを即座のリターンのない時間投資だと見なしているということです。必ずしもそのように動作するとは限りません。

最初にテストを書くreallyは、コードのこの部分で何をする必要があるかに焦点を当てます。

次に、これらを実行すると、テストで発生するバグが明らかになります。

3番目に、それらを実行すると、テストでは発生しないバグが表示され、本番環境でお尻に噛み付くことがあります。

4番目に、実行中のシステムでバグにぶつかって単体テストを作成した場合、後でそのバグを再導入することはできません。それは本当に大きな助けになります。再導入されたバグは一般的で非常に迷惑です。

第5に、コードを他の誰かに引き渡す必要がある場合、テストスイートを使用すると、作業がはるかに簡単になります。また、プロジェクトを無視して、数年後にプロジェクトに戻ってきた場合は、プロジェクトにそれほど近づくことはなく、それも役に立ちます。

私の経験は一貫して、プロジェクトの開発全体を通して、まともな単体テストを行うことで、常にプロセスがより速く、より信頼できるものになったというものです。

34
glenatron

JUnit(Javaユニットテストフレームワーク)の人たちは、テストするのが簡単すぎる場合はテストしないという哲学を持っています。私は彼らの ベストプラクティスFAQ を読むことを強くお勧めします。

TDDは、ソフトウェアを作成する別のプロセスです。ユニットテストの基本的な前提は、コードをステップ実行するデバッガーに費やす時間を短縮し、コードの変更がシステム内の他の何かを誤って破壊していないかどうかをより迅速に把握することです。これはTDDに適合します。 TDDサイクルは次のとおりです。

  1. テストを書く
  2. 失敗するのを見る(何かすることがあると証明する)
  3. テストに合格するために必要なものだけを書いてください。
  4. それが通過するのを見てください(そうです!)
  5. リファクタリング(改善する)
  6. 洗浄、すすぎ、繰り返し

TDDの適用についてあまり明白でないのは、それがあなたの書き込みコードの方法を変更することですコードが機能していることをテスト/検証する方法を自分で考えさせることで、テスト可能なコードを作成しています。また、単体テストについて話しているため、通常はコードがよりモジュール化されることになります。私にとって、モジュール化されたテスト可能なコードは、前もって大きな勝利です。

ここで、C#プロパティなどをテストする必要がありますか?次のように定義されたプロパティを想像してください:

bool IsWorthTesting {get; set;}

この時点で言語機能をテストしているため、答えは「いいえ」です。テストする価値はありません。 C#プラットフォームの人たちが正しく理解していることを信じてください。さらに、失敗した場合、それを修正するために何ができますか?

また、適切にテストするには非常に多くの労力が必要となるコードの特定の部分があることがわかります。つまり、それを行わないでください。ただし、トリッキーな問題を使用/使用するコードをテストしてください。

  • インストールが失敗した場合にのみ発生する可能性のある例外を確認しました。 Javaにはこれらがたくさんあります。インストールされたファイルをハッキングせずに失敗する可能性がない場合でも、catchブロックを書くか、チェック済み例外を宣言する必要があります。
  • ユーザーインターフェース。テスト中のコントロールを見つけ、適切なイベントを呼び出してユーザーのアクションをシミュレートすることは非常に面倒で、場合によっては不可能です。ただし、モデル/ビュー/コントローラーのパターンを使用する場合は、モデルとコントローラーがテストされていることを確認し、ビューの部分を手動でテストすることができます。
  • クライアント/サーバーの相互作用。これは単体テストではなくなり、integrationテストになりました。ネットワーク経由でメッセージを送受信する部分まですべて記述しますが、実際にはネットワーク経由ではありません。良いアプローチは、実際の通信を介して生の通信と実際に通信するコードの責任を減らすことです。単体テストコードで、通信オブジェクトをモックアウトして、サービスが期待どおりに動作していることを確認します。

信じられないかもしれませんが、TDDは持続可能な開発ペースにあなたが陥るのを助けます。それは魔法のせいではなく、フィードバックループがタイトで、本当にばかげたミスをすばやくキャッチできるためです。小さな間違いは決して大きな間違いに成長することはないので、それらの間違いを修正するコストは基本的に一定です(少なくとも計画の目的には十分です)。それを、コードビンジ/デバッグパージスプリントのバースト性と比較してください。

33
Berin Loritsch

テストのコストとバグのコストのバランスをとる必要があります。

失敗が「ファイルが見つかりません」であるファイルを開く関数の10行の単体テストを記述しても意味がありません。

複雑なデータ構造に対して複雑な処理を行う関数-明らかにそうです。

少し注意が必要です。ただし、単体テストの真の価値は特定の機能をテストすることではなく、それらの間のトリッキーな相互作用をテストすることです。したがって、1ビットのコードの変更を発見し、1000行離れた別のモジュールの一部の機能を破壊する単体テストは、コーヒーでの重みに値します。

24
Martin Beckett

テストはギャンブルです。

テストを作成することは、ユニットでバグが発生し、そのテストでバグをキャッチしない(現在、および将来のすべてのコード修正中に)コストがテストの開発コストよりも大きいことに賭けています。これらのテスト開発コストには、追加されたテストエンジニアリングの給与、市場投入までの時間の追加、他のものをコーディングしないことによる機会損失などが含まれます。

他の賭けと同様に、勝つこともあれば失うこともあります。

バグがはるかに少ない後期のソフトウェアが、最初に市場に出る迅速でバグの多いものよりも勝つことがあります。時には反対です。あなたはあなたの特定の分野の統計と、どれだけの経営者がギャンブルしたいと思っているかを見なければなりません。

バグの種類によっては、統計的に追加の特定のテストを作成する価値がないほど、作成される可能性が低いか、初期の健全性テストからそれを作成する可能性が非常に低い場合があります。ただし、バグのコストが非常に大きい(医療、核など)場合があるため、企業は負けを賭けなければなりません(保険の購入に似ています)。多くのアプリは、このような高い故障コストを持たないため、より高い不経済な保険を必要としません。他の人はします。

23
hotpaw2

私のアドバイスは、適切に動作させたいコードのみをテストすることです。

バグが多く、問題が発生するコードをテストしないでください。

11
Nick Hodges

TDDアプローチを試すべきかどうかよく疑問に思います。いい考えのように聞こえますが、正直に言って、余分な作業を正当化することはできません。

TDDと単体テストは同じものではありません。

コードを記述して、後で単体テストを追加できます。これはTDDではなく、多くの追加作業です。

TDDは、Red Lightのループでコーディングする習慣です。緑色の光。反復をリファクタリングします。

これは、まだ存在しないコードのテストを記述し、テストが失敗するのを監視し、コードを修正してテストを機能させ、コードを「適切」にすることを意味します。これはしばしばあなたの仕事を救います

TDDの利点の1つは、雑学について考える必要性が減ることです。 1つずれたエラーのようなものが消えます。それが返すリストが0または1で始まるかどうかを確認するためにAPIドキュメントを探し回る必要はありません。

8
Paul Butcher

私は、ほぼすべてをテストするシステムで作業しました。テストの注目すべき実行は、PDFおよびXLS出力コードでした。

どうして?データを収集するパーツをテストし、出力の作成に使用されるモデルを構築することができました。また、モデルのどの部分がPDFファイルに移動するかを判断する部分をテストすることもできました。 PDFが完全に主観的であるため、問題がないように見えるかどうかをテストすることはできませんでした。 PDFのすべての部分がtypical userで読み取り可能かどうかをテストすることはできませんでした。これも主観的なものであるためです。または、棒グラフと円グラフの間の選択がデータセットに対して正しかった場合。

出力が主観的なものになる場合、努力する価値のあることを実行できる単体テストはほとんどありません。

3
sal

多くの場合、「書き込み後手動テスト」は、いくつかのテストを書くよりも時間がかかりません。時間の節約は、これらのテストをいつでも再実行できるためです。

考えてみてください。テストである程度の機能カバレッジがある場合(コードカバレッジと混同しないでください)、10の機能があるとしましょう。ボタンをクリックすると、およそ10になります。 yousテストをやり直す...座ってコーヒーを飲みます。

また、ミンタをテストするhaveも行いません。重要な詳細に行きたくない場合は、機能をカバーする統合テストを作成できます。IMO、一部のユニットテストでは、コードではなく、言語とプラットフォームのテストが細かすぎます。

TL; DRメリットがあまりにも良いため、実際には決して適切ではありません

2
Steven Evers

私が遭遇した2つの非常に良い答えがここにあります:

  1. 単体テストと手動テストのタイミング
  2. 単体テストに関しては何をテストしないのですか?

知覚されるオーバーヘッドを回避するための正当化:

  • あなたの会社のための即時の時間/コスト節約
  • トラブルシューティング/保守性/拡張の潜在的な時間/コスト削減を、長期的に行った後でも。

あなたの仕事の品質の証明としてあなたの側から素晴らしい製品を残したくないですか?利己的な言葉で言えば、あなたがすることはあなたにとって良くないのですか

2
Aditya P

長期的には時間を節約できるため、プロの開発者はユニットテストを作成します。遅かれ早かれコードをテストします。ユーザーがテストしない場合、そして後でバグを修正する必要がある場合、修正するのが難しくなり、より多くの影響を及ぼします。

テストなしでバグがないコードを作成している場合は問題ありません。ただし、バグのない重要なシステムを作成できるとは思わないので、何らかの方法でテストしていると思います。

ユニットテストは、古いコードを変更またはリファクタリングする際のリグレッションを防ぐためにも重要です。 証明あなたの変更は古いコードを壊していませんが、彼らはあなたに多くの自信を与えます(もちろんそれらがパスする限り:))

私は戻って、すでに出荷したコードのテストのバッチ全体を書くことはしませんが、次に機能を変更する必要があるときは、そのモジュールまたはクラスのテストを書いてみることをお勧めします。カバレッジを最大70%取得してください+変更を適​​用する前。それがあなたを助けるかどうか見てください。

あなたがそれを試して正直に言ってそれが役に立たなかったと正直に言うことができるなら、私は彼らがアプローチを試してみる間それが少なくともあなたの価値があることを助けるのに役立つ十分な業界の証拠があると思います。

2
Steve

質問はTDDについてではなく、単体テスト全般についての質問でしたが、ほとんどの回答はプロTDDのようです。

何をユニットテストするか、何をユニットテストしないかについては、完全に客観的なルールはありません。しかし、多くのプログラマーが単体テストを行っていないように見える場合がいくつかあります。

  1. プライベートメソッド

OOPの哲学によっては、パブリックメソッドから複雑なルーチンを分離するプライベートメソッドを作成する場合があります。パブリックメソッドは通常、さまざまな場所で呼び出され、頻繁に使用されることを目的としています。プライベートメソッドはクラスまたはモジュール内の1つまたは2つのパブリックメソッドによって実際に呼び出されるのは、非常に具体的なものだけです。通常、パブリックメソッドの単体テストを作成するだけで十分ですが、マジックの一部を実行する基本的なプライベートメソッドは作成できません。何か問題が発生した場合プライベートメソッドを使用する場合、パブリックメソッドの単体テストはこれらの問題を検出するのに十分なはずです。

  1. あなたがすでに知っていることはうまくいくはずです(または誰かがテストしたもの)

新しいプログラマーの多くは、最初にテストを学んでいるときにこれに反対し、実行されているすべての行をテストする必要があると考えています。外部ライブラリを使用していて、その機能が十分にテストされ、作成者によって文書化されている場合、通常、単体テストで特定の機能をテストしても意味がありません。たとえば、その動作自体がすでにRailsで徹底的にテストされている場合でも、誰かがActiveRecordモデルがデータベースの「before_save」コールバックを使用して属性の正しい値を保持することを確認するテストを作成する場合があります。おそらく、コールバックが呼び出しているメソッドですが、コールバック動作自体は呼び出していません。インポートされたライブラリの根本的な問題は、単体テストではなく、受け入れテストによって明らかになるでしょう。

TDDを実行しているかどうかにかかわらず、どちらも適用できます。

1
Ravenstine

ケン、私、および他の多くの多くの開発者が、私たちのキャリアを通じて何度もあなたと同じ結論に達しました。

(他の多くの場合と同様に)あなたが見つけると思う真実は、アプリケーションのテストを作成するための初期投資は困難に思えるかもしれませんが、適切に作成され、コードの適切な部分を対象とする場合、実際に1トン節約できます。時間の。

私の大きな問題は、利用可能なテストフレームワークにありました。それらが私が探していたものだと本当に感じたことがなかったので、私は自分の非常にシンプルなソリューションを導入しました。それは私を回帰テストの「ダークサイド」に連れて行くのに本当に役立ちました。ここで私がしたことの基本的な疑似スニペットを共有します。うまくいけば、あなたに役立つ解決策を見つけることができます。

public interface ITest {
    public string Name {
        get;
    }
    public string Description {
        get;
    }
    public List<ITest> SubTests {
        get;
    }
    public TestResult Execute();
}

public class TestResult {
    public bool Succesful {
        get;
        set;
    }

    public string ResultMessage {
        get;
        set;
    }

    private Dictionary<ITest, TestResult> subTestResults = new Dictionary<ITest, TestResult>();
    public Dictionary<ITest, TestResult> SubTestResults {
        get {
            return subTestResults;
        }
        set {
            subTestResults = value;
        }
    }
}

その後の唯一のトリッキーな部分は、どの粒度のレベルが、どのようなプロジェクトを実行するのにも最適な「費用対効果」であると考えるかを理解することです。

アドレス帳を構築することは、明らかにエンタープライズ検索エンジンよりもはるかに少ないテストで済みますが、基本は実際には変わりません。

幸運を!

0
Adam Carstensen