テストを書くのに実際のコードと同じくらいの時間を費やすのは普通ですか?


211

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

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

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

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


20
逸話ですが、TDDでコードを書くのと同じくらいの時間をテストの作成に費やしています。コードよりもテストに多くの時間を費やしているという事実の後に、私がスリップしてテストを書くときです。
ラバーダック

10
また、コードを書くよりも読み取りに多くの時間を費やします。
するThorbjörnRavnアンデルセン

27
また、テスト実際のコードです。その部分を顧客に出荷しないだけです。
するThorbjörnRavnアンデルセン

5
理想的には、コードを書くよりも多くの時間を実行することになります。(それ以外の場合は、タスクを手動で行うだけです。)
ジョシュアテイラー

5
@RubberDuck:ここで反対の経験。時々、コードとデザインがすでにかなり整然としているのでテストを書くとき、コードとテストを書き直す必要はありません。そのため、テストの作成にかかる時間が短縮されます。それは規則ではありませんが、私にはよく起こります。
ジョルジョ

回答:


205

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

単体テストは、デバッグとテスト作業を(潜在的に自動化可能な)コードにキャプチャするため、より多くの作業がそれらに費やされることは理にかなっています。実際にかかる時間は、テストを記述せずに行うデバッグとテストよりも長くないはずです。

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

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


4
コードをテストするのが難しい理由を調べる良い機会かもしれません:)ネストされたバイナリ/ターナリ演算子のように、厳密に必要ではない複雑な多機能行を「展開」してみてください。パスの1つとしてバイナリ/三項演算子も持つ/三項演算子
ネルソン

53
私は最後の部分に同意しないようにお願いします。非常に高い単体テストのカバレッジを目指している場合は、まれであり、コードの意図された使用に対して完全な場合もあるユースケースをカバーする必要があります。これらのコーナーケースのテストを作成することは、タスク全体の中で最も時間のかかる部分である場合があります。
オットー

私はこれを別の場所で言いましたが、ほとんどのコードは一種の「パレート原理」パターンに従う傾向があるため、ユニットテストははるかに長く実行される傾向があります。ロジックの100%をカバーします(つまり、すべてのエッジケースをカバーするには、ユニットテストコードの約5倍の時間がかかります)。もちろん、フレームワークに応じて、複数のテスト用に環境を初期化して、必要なコード全体を削減できますが、それでも追加の計画が必要です。100%の信頼に近づくには、単にメインパスをテストするよりもはるかに多くの時間が必要です。
phyrfox

2
@phyrfoxこれは慎重すぎると思います。「コードの残りの99%はエッジケース」に似ています。つまり、テストの残りの99%は、これらのエッジケース用です。
モー

@Nelsonネストされた三項演算子は読みにくいと思いますが、テストが特に難しくなるとは思いません(可能なカバレッジツールの1つが見当たらない場合は、優れたカバレッジツールが表示されます)。IMO、ソフトウェアは、結合が強すぎる場合、またはハードワイヤードデータまたはパラメーターとして渡されないデータに依存している場合(たとえば、条件が現在の時刻に依存し、これがパラメーターとして渡されない場合)、テストが困難です。これは、コードがどれだけ「読み取り可能」であるかには直接関係しませんが、もちろん、他のすべてのものは同等で、読み取り可能なコードの方が優れています!
アンドレスF.

96

そうです。

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

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

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の支持者によると、実行する必要のあるテストの数と、さらに重要なこととして、特にテスト用に書き直すビジネスコードの量を減らすことで、開発プロセスを高速化します。


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


5
人も名前をnullにできることを忘れないでください。stackoverflow.com/questions/4456438/…–
psatek

1
@JacobRaihle @MainMa personNameはa のfits の値を意味すると仮定しますstringが、値にpersonName連結値を加えた値がオーバーフローしstringます。
ウォズ

@JacobRaihle:答えを編集してこの点を説明しました。脚注を参照してください。
Arseni Mourzenko

4
As a rule of thumb, if you remove a unit test, the branch coverage should decrease.上記の4つのテストをすべて作成し、3番目のテストを削除すると、カバレッジは減少しますか?
Vivek

3
「十分に長い」→「長すぎる」(ポイント4)?
パエロエベルマン

59

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

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

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

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

多くの店がTDDのクールエイドを飲んでいますが、上記のリンクが示すように、多くの研究がその利点が決定的でないことを示しています。


10
リファクタリングのポイントに対して+1。私は、10年以上にわたってパッチが適用され、旧式の製品に取り組んできました。特定のメソッドの依存関係を判断しようとするだけで1日の大部分を占め、その後、それらをモックする方法を見つけようとするとさらに時間がかかる可能性があります。5行の変更が200行を超えるテストコードを必要とし、1週間の大半を費やすことは珍しくありませんでした。
TMN

3
この。MainMaの解答テスト4では、学問の文脈の外で行われるべきではありません。実際にそれがどのように発生するかを考えてみてください。テストしないでください。ほとんどの場合、それを検出するためのコードパスはありません。適切な対応策は、フレームワークがメモリ不足の例外をスローできるようにすることです。これが実際の問題だからです。
モー

3
.equals()「Eclipseによって生成されたこれらの長いメソッドをテストする必要がなくなる」まで応援します。私はのためのテストハーネスを書いたequals()compareTo() github.com/GlenKPeterson/TestUtils 私が今までテストしたほぼすべての実装が欠けていました。正しくかつ効率的に連携して動作する場合equals()hashCode()しない場合、どのようにコレクションを使用しますか?私はあなたの答えの残りのために再び応援しており、それを支持しました。私もことを許可するいくつかの自動生成のequals()メソッドがありますテストを必要としないが、私はそれは私が神経質になり貧しい実装と非常に多くのバグを持っていました。
グレンピーターソン

1
@GlenPeterson同意します。私の同僚は、この目的のためにEqualsVerifierを作成しました。github.com/jqno/equalsverifier
Tohnmeister

@Ӎσᶎいいえ、受け入れられない入力をテストする必要があります。これが、セキュリティの悪用を見つける方法です。
gbjbaanb

11

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

物理ベースのレンダリングから式またはアルゴリズムを実装する必要がある場合、ほんのわずかなバグや不正確さで、診断がほとんど不可能なバグにつながる可能性があることがわかっているため、妄想的な単体テストに10時間を費やすことは非常に良いことです。

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


これは本当に価値のある視点です。質問に完全に回答するには、より多くのコンテキストが必要です。
CLF

3

はい、TDDingについて話している場合は正常です。テストを自動化すると、コードの目的の動作を確保できます。最初にテストを作成するとき、既存のコードに必要な動作が既にあるかどうかを判断します。

この意味は:

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

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

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

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

実際にコードを書くのに費やすよりも。

はい、それは予想される尺度です。


3

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

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

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

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


2

それは多くの人にとっては可能ですが、それは依存します。

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

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

コードを書いた後にテストを書くと、コードを簡単にテストできないことがわかります。そのため、テストの作成は難しくなり、時間がかかります。

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

コーディングにかかる​​時間と単体テストの方法についての考え方を変える必要があると思います。短期的に見てはならず、特定の機能を提供するために合計時間を比較することは決してありませんより良い/少ないバギー。

ある時点で、私たち全員が非常に優れたコードを書くことしかできないので、いくつかのツールとテクニックはスキルを向上させるのに非常に多くを提供できます。レーザー誘導式のこぎりしか持っていなかったら、家を建てることはできなかった。


2

テストを書くのに実際のコードと同じくらいの時間を費やすのは普通ですか?

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

まず第一に、ほとんどの大規模店舗がこのように機能するという意味では「正常」です。したがって、この方法が完全に見当違いで愚かであっても、大部分の大規模店舗がこのように動作するという事実は「正常」になります。

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

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

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

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


0

テストを書くのに実際のコードと同じくらいの時間を費やすのは普通ですか?

  • はい書き込むためのユニットテストコードがされた場合には(単独で、モジュールをテストする)高度に結合された(レガシー、十分な関心事の分離、欠落依存性注入はなく、開発TDD
  • はい書くための統合/受け入れテストをテストするためのロジックを経由してのみ到達可能である場合、GUIコード
  • ノー GUIコードおよび限り統合/受け入れテストを書くために分離されているビジネス・ロジック(テストは、GUIと対話する必要はありません)
  • いいえ問題、依存性注入のseperatonがある場合、ユニットテストを書き込むため、コードがした開発しtestdriven(TDD)を
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.