web-dev-qa-db-ja.com

実際のコードよりも多くの時間をテストの作成に費やすのは普通ですか?

テストは、テストしている実際のコードよりもはるかに複雑で、書くのが難しいと感じています。テストしているコードよりもテストの作成に多くの時間を費やすことは珍しいことではありません。

それは正常ですか、それとも私は何か間違っていますか?

単体テストまたはテスト駆動開発は価値がありますか? 」、「 システム自体の実装よりも機能テストの実装により多くの時間を費やしていますが、これは正常ですか? 」という質問と彼らの答えは、テストがそれだけの価値があるかどうかに関するものです(「テストの作成をすべてスキップする必要があるか」のように)。私はテストが重要であると確信していますが、実際のコードよりもテストに多くの時間を費やすことが正常であるのか、それとも私だけなのか疑問に思っています。

受け取った私の質問の意見、回答、賛成票の数から判断して、私はそれがウェブサイトの他の質問で扱われていない正当な懸念であるとしか想定できません。

216
springloaded

私はソフトウェアエンジニアリングコースで覚えています。1つは開発時間の約10%を新しいコードの作成に費やし、残りの90%はデバッグ、テスト、およびドキュメント作成に費やしていることです。

ユニットテストはデバッグとテストの努力を(潜在的に自動化可能な)コードに取り込むので、より多くの努力がそれらに費やされることは理にかなっています。実際にかかる時間は、テストを記述せずに行うデバッグおよびテストよりも長くてはなりません。

最後に、テストはドキュメントとしても機能します。ユニットテストは、コードが意図された方法で作成する必要があります。つまり、テスト(および使用法)は単純で、実装に複雑なものを含める必要があります。

テストを記述するのが難しい場合、テストするコードはおそらく使いにくいでしょう。

206
esoterik

そうです。

単体テストだけを行ったとしても、実際にテストされたコードよりも多くのコードがテスト内にあることは珍しいことではありません。何も問題はありません。

簡単なコードを考えてみましょう:

public void SayHello(string personName)
{
    if (personName == null) throw new NullArgumentException("personName");

    Console.WriteLine("Hello, {0}!", personName);
}

テストは何でしょうか?ここでテストする少なくとも4つの単純なケースがあります。

  1. 個人名はnullです。例外は実際にスローされますか?これは、少なくとも3行のテストコードを記述します。

  2. 個人名は"Jeff"です。 "Hello, Jeff!"を受け取りますか?これは、4行のテストコードです。

  3. 個人名は空の文字列です。どのような出力が期待できますか?実際の出力は何ですか?補足質問:機能要件に一致していますか?これは、単体テスト用の別の4行のコードを意味します。

  4. 人物名は文字列には十分な長さですが、"Hello, "および感嘆符と組み合わせるには長すぎます。どうなりますか?¹

これには多くのテストコードが必要です。さらに、最も基本的なコードは、テスト対象のコードに必要なオブジェクトを初期化するセットアップコードを必要とすることが多く、スタブやモックなどの作成にもつながります。

比率が非常に大きい場合は、いくつかの点を確認できます。

  • テスト間でコードの重複はありますか?それがテストコードであるという事実は、コードが同様のテスト間で複製(コピーアンドペースト)される必要があることを意味しません。そのような複製は、それらのテストの保守を困難にします。

  • 冗長なテストはありますか?経験則として、単体テストを削除すると、ブランチカバレッジは減少するはずです。そうでない場合は、パスがすでに他のテストでカバーされているため、テストが不要であることを示している可能性があります。

  • テストする必要があるコードのみをテストしていますか?サードパーティライブラリの 基になるフレームワークをテストする ではなく、プロジェクト自体のコードのみを想定しています。

スモークテスト、システムテストと統合テスト、機能テストと受け入れテスト、ストレステストと負荷テストを使用して、さらにテストコードを追加するため、実際のコードのすべてのLOCに対して4つまたは5つのLOCテストを行うことは、心配する必要のあることではありません。

TDDに関する注意

コードのテストにかかる時間を心配している場合は、コードを最初に実行し、後でテストしているため、間違っている可能性があります。この場合、TDDは、15〜45秒の反復で作業し、コードとテストを切り替えることで、役立つ場合があります。 TDDの支持者によると、実行する必要のあるテストの数と、さらに重要なことに、書き込むビジネスコードの量、特にrewriteテスト用。


n文字列の最大長 とします。 SayHelloを呼び出して、参照によって長さn-1の文字列を渡すことができます。これで、Console.WriteLineステップで、フォーマットは最終的に長さn+ 8の文字列になり、例外が発生します。おそらく、メモリの制限により、n/ 2文字を含む文字列でも例外が発生します。この4番目のテストが単体テストであるかどうか(1つのように見えますが、平均的な単体テストと比較してリソースの点ではるかに高い影響があるかどうか)と、実際のコードまたは基になるフレームワークをテストするかどうかを尋ねます。

97

ユニットテストと統合/受け入れテストの2種類のテスト戦略を区別することが重要だと思います。

場合によっては単体テストが必要ですが、それはしばしば絶望的にやりすぎです。これは、「100%カバレッジ」のように、開発者に強制される無意味なメトリックによって悪化します。 http://www.rbcs-us.com/documents/Why-Most-Unit-Testing-is-Waste.pdf はこれについて説得力のある議論を提供します。積極的な単体テストに関する次の問題を考慮してください。

  • ビジネス価値を文書化せず、単にその100%のカバレッジに近づくために存在する、無意味なテストが豊富にあります。私が作業する場所では、クラスの新しいインスタンスを作成する以外に何もしないfactoriesの単体テストを記述する必要があります。付加価値はありません。または、Eclipseによって生成された長い.equals()メソッド-これらをテストする必要はありません。
  • テストを簡単にするために、開発者は複雑なアルゴリズムをより小さなテスト可能なユニットに分割します。勝ちそうですね。共通のコードパスをたどるために12のクラスを開く必要がある場合はそうではありません。これらの場合、ユニットテストは実際にコードの可読性を低下させる可能性があります。これに関する別の問題は、コードを小さすぎる部分に分割すると、別のコードのサブセットである以上の正当性がないと思われるクラス(またはコードの部分)の大きさになってしまうことです。
  • 高度にカバーされたコードのリファクタリングは、その機能に依存するユニットテストの豊富さを維持する必要があるため、困難な場合がありますそうするだけです。これは、動作の単体テストによって悪化します。テストの一部では、クラスの共同編集者(通常はモックアウト)の相互作用も検証されます。

一方、統合/受け入れテストはソフトウェア品質の非常に重要な部分であり、私の経験では、それらを正しくするためにかなりの時間を費やす必要があります。

多くのショップがTDD kool-aidを飲んでいますが、上記のリンクが示すように、多くの研究がその利点には決定的なものがないことを示しています。

61
firtydank

一般化することはできません。

物理ベースのレンダリングから数式またはアルゴリズムを実装する必要がある場合、わずかなバグまたは不正確さが診断にほとんど不可能なバグにつながる可能性があることを知っているので、パラノイアユニットテストに10時間を費やすことは非常によくあります。 。

コードのいくつかの行を論理的にグループ化して名前を付けたいだけで、ファイルスコープでのみ使用する場合は、まったくテストしない可能性があります(すべての関数のテストを書くことを主張した場合、例外なく、プログラマーはフォールバックする可能性がありますできる限り少ない関数を書くことへ)。

12
phresnel

それが最も重要な部分だと思います。

単体テストは常に「正しく機能するかどうかを確認する」ことではなく、学習することです。十分なものをテストすると、それは脳に「ハードコード」され、最終的にユニットテスト時間を短縮し、完了するまで何もテストせずにクラスとメソッド全体を書くことができます。

これが、このページの他の回答の1つが、「コース」では90%のテストを行ったと述べている理由です。なぜなら、誰もが目的の警告を学ぶ必要があったからです。

単体テストは文字通りスキルを向上させるので、時間の非常に貴重な使用であるだけでなく、自分のコードをもう一度調べて、途中で論理的な間違いを見つけるのに良い方法です。

4
Jesse

はい、TDDingについて話しているのは正常です。テストを自動化したら、コードの望ましい動作を保護します。最初にテストを作成するときに、既存のコードにすでに希望する動作があるかどうかを判断します。

この意味は:

  • 失敗するテストを作成する場合、機能する最も簡単なことでコードを修正する方が、テストを作成するよりも短くなります。
  • 合格したテストを作成する場合、追加のコードを書く必要はなく、コードを書くよりもテストを書くためにより多くの時間を費やします。

(これは、後続のコードの作成に費やす時間の削減を目的とするコードリファクタリングを考慮していません。後続のテストの作成に費やす時間の削減を目的とするテストリファクタリングによってバランスがとられています。)

はい、あなたが事実の後でテストを書くことについて話しているなら、あなたはより多くの時間を費やすでしょう:

  • 望ましい行動を決定する。
  • 望ましい動作をテストする方法を決定する。
  • テストを記述できるようにするためのコード依存関係の実現。
  • 失敗したテストの修正コード。

実際にコードを書くよりも。

そう、それは期待された測定です。

3

実際のコードよりも多くの時間をテストの作成に費やすのは普通ですか?

はい、そうです。いくつかの注意点があります。

まず第一に、ほとんどの大きな店がこのように機能するという意味で「通常」です。したがって、この方法が完全に見当違いでばかげていたとしても、ほとんどの大きな店がこのように機能するという事実は、それを「通常」にします。

これは、テストが間違っていることを意味するものではありません。私はテストのない環境と強迫的テストのある環境で働いてきましたが、強迫的テストでさえテストなしよりも優れていたと言えます。

そして、私はまだTDDを実行していません(ご存知のように、将来はそうかもしれません)が、実際のアプリケーションではなくテストを実行することにより、編集、実行、デバッグのサイクルの大部分を実行しているため、当然、私は多くの作業をしています私のテストでは、実際のアプリケーションを実行しなければならないことを可能な限り回避します。

ただし、過度のテスト、特にテストの維持の維持には危険があることに注意してください。 (特にこれを指摘するために、主にこの回答を書いています。)

Roy Osheroveの序文The Art of Unit Testing(Manning、2009)著者は、失敗したプロジェクトに参加したことを認めています大部分は、不適切に設計された単体テストによって課された途方もない開発の負担によるものであり、開発作業の期間中維持する必要がありました。したがって、テストを維持する以外に何もしないことに時間を費やしている場合、これは「正常」なので、必ずしも正しい方向に進んでいるとは限りません。開発作業が不健全なモードに入った可能性があり、プロジェクトを保存するために、テスト方法の根本的な見直しが必要になる場合があります。

2
Mike Nakis

それは多くの人のためにあるかもしれませんが、それは依存します。

最初にテスト(TDD)を作成している場合、テストの作成に費やされた時間にコードの作成に実際に役立つ時間が重複している可能性があります。考慮してください:

  • 結果と入力の決定(パラメーター)
  • 命名規則
  • 構造-物を置く場所。
  • 平凡な古い考え

コードを記述した後でテストを記述する場合、コードを簡単にテストできない場合があるため、テストの記述は困難または時間がかかります。

ほとんどのプログラマーはテストよりもずっと長い間コードを書いているので、私はそれらのほとんどが流暢ではないと予想します。さらに、テストフレームワークの理解と利用にかかる時間を追加できます。

コーディングにかかる​​時間とユニットテストの関与について、考え方を変える必要があると思います。短期間でそれを見てはならず、特定の機能を提供するための合計時間を決して比較しないでください。優れた/バグの少ないコードを書くだけでなく、変更しやすく、それでも作りやすいコードを検討する必要があるためです。バギーが良い/少ない。

ある時点で、私たちはすべて、非常に優れたコードを書くことができるだけです。そのため、一部のツールやテクニックは、スキルを向上させるために非常に多くのことを提供できます。レーザー誘導式のこぎりだけがあったら、家を建てることはできません。

2
JeffO

実際のコードよりも多くの時間をテストの作成に費やすことは正常ですか?

  • はい書き込み nit-tests (モジュールを個別にテスト)コードが高度に結合(レガシー、十分ではない 懸念の分離 、欠落 依存性注入 、ない tddが開発された
  • はい書き込み integration/acceptance-tests テストするロジックがgui-code
  • いいえgui-codeとbusiness-logicが分離されている限り、統合/受け入れテストを記述します(テストはGUIと対話する必要はありません)
  • いいえ懸念事項、依存性注入、コードがテスト駆動開発された場合、ユニットテストを作成するために開発されました(tdd)
1
k3b