プライベートコードでユニットテストを実施するにはどうすればよいですか?


15

私は自分のワークグループでユニットテストを提唱しようとしていますが、私がよく受ける反対意見は、外部エクスポートされたAPI(これはシステムの最小限かつ重要ではない部分のみ)にのみ使用すべきであり、内部およびプライベートでは使用しないことですコード(機能テストのみになりました)。

ユニットテストはすべてのコードに適用できるし、そうすべきだと思いますが、同僚をどのように説得できますか?


3
テストする必要があると感じるプライベートメソッドがある場合、それは多くの場合、コードがSRPに違反していることの兆候であり、そこには別のクラスが独自に抽出され、テストされることを叫んでいます。
Paddyslacker

@Paddyslacker:すべてのコードをテストする必要があると感じています。単一の責任原則に従うコードユニットがユニットテストの対象とならない理由がわかりません...
Wizard79

4
@lorenzo、あなたは私のポイントを逃しました。うまくいかなかったのかもしれません。これらのプライベートメソッドを別のクラスに抽出する場合、元のクラスからアクセスできる必要があります。メソッドは公開されているため、テストする必要があります。私は、それらがテストされるべきではないことを暗示していませんでした。メソッドを直接テストする必要があると感じたら、それらはプライベートではない可能性が高いことを暗示していました。
Paddyslacker

@Paddyslacker:プライベートメソッドも直接テストする必要があると感じています。なぜ彼らはプライベートであるべきではないと思うのですか?
Wizard79

6
プライベートメソッドをテストすることにより、抽象化を壊しています。単体テストでは、実装ではなく、状態や動作をテストする必要があります。あなたの例/シナリオは、プライベートコードの結果が何であるかを検証できるはずです-もしそれが難しいと思うなら、Paddyslackerが言うように、それはあなたがSRPに違反していることを意味するかもしれません。ただし、コードが実行していることを完全に代表するように例を抽出していないことも意味します。
FinnNk

回答:


9

あなたの同僚は、本当の単体テストと統合テストを混同しているかもしれません。製品がAPIである(またはAPIを持っている)場合、統合テストはNUnitテストケースとしてプログラムできます。一部の人々は、それらがユニットテストであると誤って信じています。

次の方法で同僚を説得することができます(このことを既に知っていると確信しています。同僚にそれを指摘するだけで助けになるかもしれません)。

  • テストカバレッジ。これらの統合テストの実際のテストカバレッジ率を測定します。これは、テストカバレッジを実行したことがない人のための現実チェックです。入力が数層離れている場合、すべての論理パスを実行するのは難しいため、テストカバレッジは20%から50%の間で最大になります。より多くのカバレッジを得るには、同僚が実際の独立した単体テストを作成する必要があります。
  • 設定。テスト対象の同じソフトウェアを展開すると、おそらく、異なる環境でテストを実行することがいかに難しいかを同僚に示すことができます。さまざまなファイルへのパス、DB接続文字列、リモートサービスのURLなど、すべてが加算されます。
  • 実行時間。テストが真の単体テストであり、メモリ内で実行できる場合を除き、実行には多くの時間がかかります。

12

内部/プライベートコードで単体テストを使用する理由は、外部でサポートされているAPIの場合とまったく同じです。

  • それらはバグの再発を防ぎます(ユニットテストは回帰テストスイートの一部を形成します)。
  • 彼らは、コードが機能することを(実行形式で!)文書化します。
  • これらは、「コードが機能する」という意味の実行可能な定義を提供します。
  • これらは、コードが実際に仕様と一致することを実証する自動化された手段を提供します(上記のポイントで定義)。
  • これらは、予期しない入力が存在する場合にユニット/クラス/モジュール/関数/メソッドがどのように失敗するかを示しています。
  • 彼らは、ユニットの使用方法の例を提供します。これは、新しいチームメンバーのための優れたドキュメントです。

8

あなたが私があなたがそれを意味すると思う方法でプライベートを意味するなら、いいえ-あなたはそれをユニットテストするべきではありません。観察可能な動作/状態の単体テストのみを行う必要があります。TDDの「赤-緑-リファクタリング」サイクルの背後にあるポイントを見逃している可能性があります(最初にテストを行っていない場合は、同じ原則が適用されます)。テストが作成されて合格すると、リファクタリングを実行している間にテストが変更されないようにします。プライベート機能の単体テストを余儀なくされている場合は、おそらく公開機能に関する単体テストに欠陥があることを意味します。パブリックコードの周りにテストを書くのが難しくて複雑な場合は、クラスが多すぎるか、問題が明確に定義されていない可能性があります。

さらに悪いことに、ユニットテストは時間の経過とともにボールとチェーンになり、価値をまったく追加せずに速度が低下します(最適化や重複の削除など、実装を変更してもユニットテストに影響はありません)。ただし、内部コードは、動作/状態が(制限された方法で)観察可能であるため、ユニットテストする必要があります。

ユニットテストを初めて行ったとき、私はあらゆる種類のトリックを使ってプライベートなものをユニットテストしましたが、今では、数年後には時間の無駄よりも悪いと感じています。

ここにちょっと馬鹿げた例がありますが、もちろん実際にはこれらよりも多くのテストがあります:

文字列のソートされたリストを返すクラスがあるとしましょう-結果が実際にそのリストをソートする方法ではなく、ソートされていることを確認する必要があります。リストをソートするだけの単一のアルゴリズムで実装を開始できます。それが完了したら、並べ替えアルゴリズムを変更しても、テストを変更する必要はありません。この時点で、単一のテストがあります(ソートがクラスに埋め込まれていると仮定):

  1. 結果はソートされていますか?

2つのアルゴリズムが必要だとすると(おそらく1つは状況によってはより効率的ですが、他のアルゴリズムはそうではありません)、各アルゴリズムは異なるクラスによって提供され(一般的にはそうである必要があります)、クラスはそれらから選択します-これが起こっていることを確認できますモックを使用して選択したシナリオですが、元のテストはまだ有効であり、観察可能な動作/状態を確認するだけなので、変更する必要はありません。最終的に3つのテストになります。

  1. 結果はソートされていますか?
  2. シナリオ(最初のリストがほぼソートされているとしましょう)が与えられた場合、アルゴリズムXを使用して文字列をソートするクラスが呼び出されますか?
  3. シナリオ(最初のリストはランダムな順序です)が与えられた場合、アルゴリズムYを使用して文字列を並べ替えるクラスが呼び出されますか?

別の方法は、クラス内でプライベートコードのテストを開始することです。これからは何も得られません。上記のテストは、ユニットテストに関する限り、知っておく必要のあるすべてを教えてくれます。プライベートテストを追加することで、自分でストレートジャケットを作成できますが、結果が並べ替えられたことだけでなく、並べ替え方法もチェックした場合、さらに多くの作業が必要になります。

(このタイプの)テストは、動作が変更されたときにのみ変更され、プライベートコードに対するテストの作成を開始する必要があります。


1
「プライベート」の意味について誤解があるのか​​もしれません。私たちのシステムでは、コードの99%が「プライベート」であり、システムのコンポーネントの1つを自動化/リモート制御するための小さなAPIがあります。つまり、他のすべてのモジュールのコードを単体テストします。
Wizard79

4

もう1つの理由は次のとおりです。仮に、外部APIの単体テストとプライベートパーツのどちらかを選択する必要がある場合、プライベートパーツを選択します。

すべてのプライベートパーツがテストでカバーされる場合、これらのプライベートパーツで構成されるAPIもほぼ100%をカバーする必要があり、上位層だけを除外します。しかし、それは薄い層である可能性があります。

一方、APIをテストするだけの場合、可能なすべてのコードパスを完全にカバーするのは非常に困難です。


「他方では...」+1しかし、他に何もなければ、失敗が最も傷つくテストを追加します。
トニー・エニス

2

時間の無駄(「別の金makingけプロジェクトをコーディングできたかもしれません!」)または再帰的(「そして、テストケースのテストケースを書く必要がある!」)私は両方を言って有罪です。

初めてバグを発見したとき、あなたは完璧ではないという真実に直面しなければなりません(プログラマーがどれほど早く忘れるのか!)、そして「うーん」と言います。


単体テストのもう1つの側面は、テスト可能なコードを作成する必要があることです。一部のコードは簡単にテスト可能であり、一部のコードは優れたプログラマーを「うーん」とは思わないことに気づきます。


ユニットテストが外部向けAPIにのみ役立つ理由を同僚に尋ねましたか?


単体テストの価値を示す1つの方法は、厄介なバグが発生するのを待ってから、単体テストがどのようにそれを防ぐことができたかを示すことです。それは彼らの顔にそれをこすり付けることではなく、彼らの心の中で、ユニットテストをアイボリータワー理論から溝の中の現実に移すことです。

別の方法は、同じエラーが2回発生するまで待つことです。「うーん、ボス、先週の問題の後にヌルをテストするコードを追加しましたが、今回はユーザーが空のものを入力しました!」


例でリード。コードの単体テストを作成し、上司に値を示します。その後、上司がいつか昼食のためにピザを呼び出してプレゼンテーションを行うかどうかを確認します。


最後に、prodにプッシュしようとしているときに感じる安心感を伝えることはできません。ユニットテストから緑色のバーが表示されます。


2

プライベートコードの2種類があります:公共のコード(またはパブリックコード(または...)によって呼び出される民間のコードによって呼び出されるプライベートコード)によって呼び出される民間コードとプライベートコード、最終的に公衆によって呼び出されませコード。

前者はすでに公開コードのテストでテストされています。呼び出されない後者缶まったくひいてはがテストされていない、削除する必要があります。

TDDを実行すると、テストされていないプライベートコードが存在することは不可能であることに注意してください。


私たちのシステムでは、コードの99%が第3種です。プライベートであり、パブリックコードによって呼び出されることはなく、システムに不可欠です(システムの最小限の部分にのみ、外部のパブリックAPIがあります)。
Wizard79

1
「TDDを行うとき、テストされていないプライベートコードが存在することは不可能であることに注意してください。」<-テストが特定のブランチをカバーする唯一のテストであることを知らずに、テストケースを削除します。OK、それはもっと「現在テストされていない」コードですが、そのコードを変更する後の些細なリファクタリングを見るのは十分簡単です...あなたのテストスイートだけはもはやそれをカバーしていません。
フランクシェラー

2

ユニットテストとは、コードのユニットをテストすることです。ユニットが何であるかを定義するのはあなた次第です。同僚は、ユニットをAPI要素として定義します。

とにかく、APIをテストすると、プライベートコードも実行されるはずです。コードカバレッジを単体テストの進捗状況の指標として定義すると、すべてのコードをテストすることになります。コードの一部に到達していない場合、同僚に3つの選択肢を与えます。

  • その部分をカバーする別のテストケースを定義し、
  • 単体テストのコンテキストではカバーできないが、他の状況ではカバーする必要がある理由を正当化するためにコードを分析します。
  • 正当化もカバーもされていないデッドコードを削除します。

このシステムでは、APIは最小限の部分にすぎず、サードパーティアプリケーションの自動化/リモート制御が可能です。1%のコードカバレッジのAPIアカウントのみをテストしています...
Wizard79
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.