web-dev-qa-db-ja.com

すべてのメソッドをテストする必要がありますか?

それで今日、私はチームメイトとユニットテストについて話し合いました。すべては彼が私に「そのクラスのテストはどこにあるのか、私はたった1つしか見ないのですか?」と私に尋ねたときに始まりました。クラス全体はマネージャー(または、そのように呼び出したい場合はサービス)であり、ほとんどすべてのメソッドは単にデータをDAOに委任していたため、次のようになります。

SomeClass getSomething(parameters) {
    return myDao.findSomethingBySomething(parameters);
}

一種のボイラープレートはロジックがありません(または、少なくとも私はそのような単純な委譲をロジックとは見なしません)が、ほとんどの場合(レイヤー分離など)には有用なボイラープレートです。そして、私はそれを単体テストすべきかどうかというかなり長い議論をしました(DAOを完全に単体テストしたことは言及する価値があると思います)。彼の主な議論は、それがTDDではないこと(明らかに)であり、誰かがこのメソッドの機能を確認するためにテストを見たいと思うかもしれない(私はそれがどのようにより明白であるかわかりません)、または将来誰かが実装し、それに新しい(または「any」のような)ロジックを追加します(この場合、誰かが単にlogicをテストする必要があると思います)。

しかし、これは私に考えさせられました。最高のテストカバレッジ%を目指して努力すべきですか?それともそれは単に芸術のための芸術なのでしょうか?私は単に次のようなものをテストする背後に何の理由も見ていません:

  • ゲッターとセッター(実際にロジックがある場合を除く)
  • 「ボイラープレート」コード

明らかに、このような方法(モックを使用)のテストでは1分もかかりませんが、それでも無駄な時間はなく、CIごとに1ミリ秒長くなります。

コードのすべての行(またはできるだけ多くの行)をテストする必要がある理由に、「可燃性」ではない合理的な理由はありますか?

63
Zenzen

私はケントベックの経験則に従っています。

壊れる可能性のあるすべてのものをテストします。

もちろん、それはある程度主観的です。私にとって、ささいなゲッター/セッターと上記のようなワンライナーは通常、それだけの価値はありません。しかし、繰り返しになりますが、私はほとんどの時間をレガシーコードの単体テストの作成に費やしており、Nice greenfield TDDプロジェクトを夢見ているだけです...そのようなプロジェクトでは、ルールが異なります。レガシーコードの主な目的は、できるだけ少ない労力で多くの範囲をカバーすることです。そのため、ユニットテストは、用語について熟知している場合、統合テストのように、より高レベルでより複雑になる傾向があります。そして、全体的なコードカバレッジを0%から上げるのに苦労している場合、または25%を超えてうまく管理できている場合、ゲッターとセッターのユニットテストは、心配することの最も少ないものです。

グリーンフィールドTDDプロジェクトのOTOHでは、そのような方法でもテストを作成することはより現実的かもしれません。特に、「この1行は専用のテストに値するのか?」と疑問に思う前に、すでにテストを作成しているためです。そして、少なくともこれらのテストは書くのが簡単で、実行が速いので、どちらの方法でも大したことではありません。

49
Péter Török

ユニットテストにはいくつかの種類があります。

  • 州ベース。あなたは行動し、オブジェクトの状態に対してアサートします。例えば。デポジットをします。次に、残高が増えているかどうかを確認します。
  • 戻り値に基づく。あなたは行動し、戻り値に対して断言します。
  • 相互作用ベース。オブジェクトが別のオブジェクトを呼び出したことを確認します。これはあなたの例であなたがやっていることのようです。

最初にテストを作成する場合は、データアクセスレイヤーを呼び出すことが予想されるため、より理にかなっています。テストは最初は失敗します。次に、テストに合格するための製品コードを記述します。

理想的には論理コードをテストする必要がありますが、相互作用(他のオブジェクトを呼び出すオブジェクト)も同様に重要です。あなたの場合、私は

  • 渡された正確なパラメーターでデータアクセスレイヤーを呼び出したことを確認します。
  • 一度だけ呼び出されたことを確認してください。
  • データアクセスレイヤーから提供されたものを正確に返すことを確認します。それ以外の場合はnullを返すこともできます。

現在、そこにはロジックはありませんが、常にそうであるとは限りません。

ただし、このメソッドにはロジックがなく、同じままであると確信している場合は、コンシューマーから直接データアクセスレイヤーを呼び出すことを検討します。これは、チームの他のメンバーが同じページにいる場合にのみ行います。 「ドメインレイヤーを無視して、データアクセスレイヤーを直接呼び出すだけで問題ありません」と言って、チームに間違ったメッセージを送信したくない場合。

この方法の統合テストがある場合は、他のコンポーネントのテストにも集中します。しかし、確かな統合テストを行っている会社はまだ見ていません。

このすべてを言った-私は盲目的にすべてをテストすることはありません。ホットスポット(複雑性が高く、破損のリスクが高いコンポーネント)を確立します。次に、これらのコンポーネントに集中します。残りの10%がシステムのコアロジックを表し、それらが複雑であるためにユニットテストの対象外である場合、コードベースの90%が非常に単純であり、ユニットテストでカバーされるコードベースを使用しても意味がありません。

最後に、この方法をテストする利点は何ですか?これが機能しない場合の影響は何ですか?彼らは破滅的ですか?高いコードカバレッジを獲得しようと努力しないでください。コードカバレッジは、優れた単体テストスイートの副産物である必要があります。たとえば、ツリーをウォークしてこのメ​​ソッドを100%カバーする1つのテストを記述したり、100%をカバーする3つの単体テストを記述したりできます。違いは、ツリーを単に歩くのではなく、3つのテストを記述してEdgeケースをテストすることです。

13
CodeART

ソフトウェアの品質について考える良い方法は次のとおりです。

  1. 型チェックは問題の一部を処理しています。
  2. テストは残りを処理します

ボイラープレート関数と自明な関数の場合は、型チェックを使用してそれを行うことができ、残りの場合はテストケースが必要です。

9
tp1

私の意見では、循環的複雑度はパラメーターです。メソッドが十分に複雑でない場合(ゲッターやセッターなど)。単体テストは必要ありません。 McCabeのCyclomatic Complexityレベルは1以上である必要があります。別の単語では、最低1つのブロックステートメントが必要です。

6
Fırat KÜÇÜK

TDD(そしていくつかの例外はあります)

議論の余地はありますが、この質問に「いいえ」と答えた人は誰でもTDDの基本的な概念を見逃していると私は主張します。

私にとって、TDDに従っている場合、答えははっきりとyesです。そうでない場合は、もっともらしい答えはありません。

TDDのDDD

TDDは、主な利点があるとよく言われます。

  • ディフェンス
    • コードは変更される可能性がありますですがその動作ではありませんを保証します。
    • これにより、これまでになく重要なリファクタリングの練習が可能になります。
    • このTDDを獲得するかどうか。
  • デザイン
    • あなた指定何をすべきか、どのように振る舞うべきか実装する前にそれ。
    • これは、多くの場合、より情報に富んだ実装の決定を意味します。
  • ドキュメント
    • テストスイートは仕様(要件)ドキュメントとして機能します。
    • そのような目的でテストを使用することは、ドキュメントと実装が常に一貫した状態にあることを意味します-1つの変更は他の変更を意味します。要件と設計を別のWord文書に保存する場合と比較してください。

責任と実装を分離する

プログラマーとして、属性を重要なものと考え、ゲッターとセッターをある種のオーバーヘッドと考えるのはひどく魅力的です。

ただし、属性は実装の詳細であり、セッターとゲッターは実際にプログラムを機能させる契約上のインターフェースです。

オブジェクトは次のように綴ることがはるかに重要です。

クライアントが状態を変更できるようにする

そして

クライアントがその状態を照会できるようにする

次に、この状態が実際に格納される方法(属性が最も一般的ですが、唯一の方法ではありません)。

次のようなテスト

_(The Painter class) should store the provided colour
_

tDDのdocumentation部分にとって重要です。

最終的な実装が取るに足らない(属性)ものであり、defenceのメリットがないという事実は、テストを作成するときにはわかりません。

往復エンジニアリングの欠如...

システム開発の世界における重要な問題の1つは、往復のエンジニアリングの欠如です。1 -システムの開発プロセスは、分離されたサブプロセスに断片化され、そのアーティファクト(ドキュメント、コード)は一貫性がないことがよくあります。

1ブロディー、マイケルL.「ジョンミロポーロス:概念モデリングの種を縫う」。概念モデリング:基礎とアプリケーション。スプリンガーベルリンハイデルベルク、2009年。

...そしてTDDがそれを解決する方法

システムとそのコードの仕様が常に一貫していることを保証するのは、TDDのdocumentation部分です。

最初に設計し、後で実装する

TDD内では、まず不合格の受け入れテストを記述してから、合格するコードを記述します。

上位レベルのBDD内では、最初にシナリオを記述してから、それを通過させます。

セッターとゲッターを除外する必要があるのはなぜですか?

理論的には、TDD内で1人がテストを記述し、もう1人が合格するコードを実装することは完全に可能です。

だから自問してください:

クラスのテストを書いている人がゲッターとセッターに言及すべきかどうか。

ゲッターとセッターはクラスへのパブリックインターフェイスであるため、答えは明らかにyesです。そうでない場合、オブジェクトの状態を設定または照会する方法はありません。

明らかに、最初にコードを書く場合、答えはそれほど明確ではないかもしれません。

例外

このルールには明らかな例外がいくつかあります。明確な実装の詳細であり、明らかにシステムの設計の一部ではない関数です。

たとえば、ローカルメソッド 'B()':

_function A() {

    // B() will be called here    

    function B() {
        ...
    }
} 
_

またはプライベート関数square()はこちら:

_class Something {
private:
    square() {...}
public:
    addAndSquare() {...}
    substractAndSquare() {...}
}
_

または、システムコンポーネントのデザインでスペルを必要とするpublicインターフェイスの一部ではないその他の関数。

3
Izhaki

哲学的な疑問に直面したら、運転の要件に戻ります。

合理的な信頼性のあるソフトウェアを競争力のあるコストで生産するという目標はありますか?

それとも、ほとんどコストに関係なく、可能な限り最高の信頼性のソフトウェアを生産することですか?

ある程度までは、品質と開発速度/コストの2つの目標が一致します。つまり、欠陥の修正よりもテストの作成に費やす時間を短縮できます。

しかし、それ以降は、そうではありません。たとえば、開発者ごとに1か月に1つ報告されたバグにアクセスすることはそれほど難しくありません。それを2か月に1度に半分にしても、1日か2日の予算が解放されるだけであり、多くの追加テストを行っても、不良率は半減しないでしょう。したがって、これは単純なwin/winではありません。顧客への欠陥コストに基づいてそれを正当化する必要があります。

このコストは変動します(そして、あなたが邪悪になりたい場合は、市場または訴訟を通じて、それらのコストをあなたに強制する能力も変動します)。あなたは悪になりたくないので、それらのコストを全額カウントします。時には、いくつかのテストはまだそれらの存在によって世界を貧しくします。

つまり、旅客機のフライトソフトウェアと同じ標準を社内のWebサイトに盲目的に適用しようとすると、廃業するか、または刑務所に入ります。

1
soru

それはトリッキーな質問です。

厳密に言えば、それは必要ではないと私は言うでしょう。ビジネス要件がポジティブおよびネガティブシナリオで意図したとおりに機能することを保証するBDDスタイルのユニットおよびシステムレベルのテストを記述する方がよいでしょう。

つまり、メソッドがこれらのテストケースでカバーされていない場合、そもそもなぜそれが存在するのか、そしてそれが必要であるのか、またはドキュメントやユーザーストーリーに反映されていないコードに隠された要件があるのか​​を質問する必要があります。 BDDスタイルのテストケースでエンコードする必要があります。

個人的には、ラインごとのカバレッジを85〜95%に保ち、メインラインへのチェックインをゲートして、既存のユニットテストカバレッジがすべてのコードファイルでこのレベルに達し、ファイルがカバーされないようにします。

最善のテストプラクティスに従っていると仮定すると、カバレッジのために、難しいコードや簡単なコードの追加のカバレッジを取得する方法を理解するために開発者に時間を浪費させることなく、十分なカバレッジが得られます。

0
Keith Brings

これに対するあなたの答えはあなたの哲学に依存します(シカゴ対ロンドンであると信じていますか?誰かがそれを調べると確信しています)。陪審員は、最も時間効率の良いアプローチでこれについてまだ結論を出していません(結局のところ、これが修正に費やす時間が減ったことの最大の要因です)。

いくつかのアプローチは、パブリックインターフェイスのみをテストすると言い、他のアプローチは、すべての関数の各関数呼び出しの順序をテストすると言っています。多くの聖なる戦争が繰り広げられてきました。私のアドバイスは、両方のアプローチを試すことです。コードのユニットを選択して、Xのように、Yのように実行します。数か月のテストと統合の後で、ニーズに合ったコードを確認します。

0
anon