プライベートメソッドの単体テストが悪い習慣と見なされるのはなぜですか?


15

環境:

現在、Pythonで小さなプロジェクトに取り組んでいます。私は一般的に文書化されているいくつかのパブリックメソッドで私のクラスを構造化が、主に高レベルの概念を扱う(クラスのユーザーが知っていて、使用すべきか)、そしてたくさんの隠された(アンダースコアで始まる)を担当している方法を複雑または低レベルの処理。

コードに信頼を置き、その後の変更によって以前の動作が損なわれないようにするためには、テストが不可欠であることを知っています。

問題:

信頼できるベースでより高いレベルのパブリックメソッドを構築するために、私は一般的にプライベートメソッドをテストします。コードの変更によってリグレッションが発生したかどうか、どこで発生したかを確認する方が簡単です。これは、これらの内部テストがマイナーリビジョンで壊れる可能性があり、修正/交換する必要があることを意味します

しかし、私は、プライベートメソッドのユニットテストが、少なくとも異議のある概念であるか、またはより頻繁に悪い習慣と見なされていることも知っています。その理由:公の行動のみをテストする必要があります参照

質問:

私は次のベストプラクティスに注意を払い、理解したいと思います。

  • プライベート/隠しメソッドでユニットテストを使用するのはなぜ悪いのですか(リスクは何ですか)?
  • パブリックメソッドが低レベルおよび/または複雑な処理を使用できる場合のベストプラクティスは何ですか?

精度:

  • それは質問する方法ではありません。Pythonにはプライバシーの真の概念がなく、隠されたメソッドは単にリストされていませんが、名前がわかっている場合に使用できます
  • 私はプログラミングのルールとパターンを教えたことはありません。私の最後のクラスは80年代からです...私は主に試行錯誤とインターネットでの参照によって言語を学びました(何年もの間、Stack Exchangeが私のお気に入りです)


2
OP、プライベートメソッドのテストが「悪い」と見なされたことをどこで聞いたり読んだりしましたか?単体テストにはさまざまな方法があります。ユニットテスト、ブラックボックステスト、ホワイトボックステストを参照してください。
John Wu

@JohnWu:ホワイトボックステストとブラックボックステストの違いを知っています。しかし、ホワイトボックステストでも、プライベートメソッドをテストする必要があることが、設計上の問題のヒントになっているようです。私の質問は、私がそこに落ちるときの最良の道は何かを理解しようとする試みです...
セルジュバレスタ

2
繰り返しますが、ホワイトボックステストでも、プライベートメソッドをテストする必要があることが設計上の問題のヒントになるということをどこで聞いたり読んだりしましたか?答える前に、その信念の根拠を理解したいと思います。
John Wu、

言い換えると、@ SergeBallestaは、プライベートメソッドのテストは悪い習慣であると信じるようになった記事にいくつかの言及を入れます。次に、なぜそれらを信じたのか説明してください。
Laiv

回答:


17

いくつかの理由:

  1. 通常、クラスのプライベートメソッドをテストしたい場合、それはデザインの匂いです(icebergクラス、再利用可能なパブリックコンポーネントが不十分など)。ほとんどの場合、「大きな」問題が発生します。

  2. パブリックインターフェースを介してそれらをテストできます(クライアントがそれらを呼び出す/使用する方法であるため、テストする方法です)。プライベートメソッドのすべてのテストに合格すると、緑色のライトが表示され、誤った安心感を得ることができます。パブリックインターフェイスを介してプライベート関数のエッジケースをテストする方がはるかに優れています。

  3. プライベートメソッドをテストすることにより、テストの重複(外観が非常によく似たテスト)のリスクが高まります。要件が変わると、必要以上のテストが失敗するため、これは大きな影響を及ぼします。また、テストスイートが原因でリファクタリングが困難な状況に置かれる可能性もあります。これは、テストスイートが安全に再設計およびリファクタリングを行うために存在するため、究極の皮肉です!

まだプライベートパーツをテストしたい場合のヒント(気になる場合は使用しないでください。YMMVですが、以前はうまく機能していました):確認のためにプライベート関数の単体テストを作成する場合があります。彼らはあなたが彼らがそうであると考える方法で正確に働いています(特にあなたが言語に慣れていない場合)。ただし、それらが機能することを確認したら、テストを削除し、常に公開テストが確実に行われ、誰かがプライベート関数に悪質な変更を加えた場合にキャッチされることを確認してください。

プライベートメソッドをテストするタイミング:この回答は(ある程度)普及しているため、「ベストプラクティス」は常に「ベストプラクティス」であることに言及する義務があります。それは、独断的または盲目的にそれを行うべきだという意味ではありません。プライベートメソッドをテストする必要があり、正当な理由(レガシーアプリケーションの特性テストを作成しているなど)がある場合は、プライベートメソッドをテストします。特定の状況は常に、一般的なルールやベストプラクティスよりも優先されます。うまくいかない可能性があるいくつかのことに注意してください(上記を参照)。

私はSOについてこれについて詳しく説明する答えがありますが、ここでは繰り返しません:https : //stackoverflow.com/questions/105007/should-i-test-private-methods-or-only-public-ones/ 47401015#47401015


4
理由1:曖昧。理由2:プライベートヘルパーメソッドをパブリックAPIの一部にしない場合はどうなりますか?理由3:クラスを適切に設計していない場合。あなたの最後のヒント:私が書いた方法が機能することを証明する完全に良いテストをなぜ削除するのですか?
ロバートハーヴェイ

4
@RobertHarvey理由2:パブリックAPIを介して間接的にアクセスできる!=パブリックAPIの一部であること。プライベートAPIがパブリックAPIを介してテストできない場合、おそらくデッドコードであり、削除する必要がありますか?または、クラスは実際に氷山であり(理由1)、リファクタリングする必要があります。
Frax

5
@RobertHarveyは、パブリックAPIを介してプライベート関数をテストできない場合、便利な目的を果たしていないため、削除してください。
デビッドアルノ

1
@RobertHarvey 1:デザインの匂いは常に多少主観的/曖昧なので、確かに。しかし、私はアンチパターンのいくつかの具体的な例を挙げました、そして私のSOの答えにはより詳細があります。2:プライベートメソッドをパブリックAPIの一部にすることはできません(定義上:それらはプライベートです)...そのため、あなたの質問にはあまり意味がないと思います。私はあなたのようなものがあればという時点で取得しようとしているbin_search(arr, item)(パブリック)とbin_search(arr, item, low, hi)(その後、すべてのあなたがテストする必要があるが、公共面する1である、(プライベートを、ビン検索を行うには多くの方法がある)bin_search(arr, item)
マット・Messersmith

1
@RobertHarvey 3:最初に、私はリスクではなく、保証すると述べました。次に、「適切に機能すれば機能する」と主張することは、自己実現的です。たとえば、「適切に実行すれば、単一の関数でオペレーティングシステムを作成できます」。これは誤りではありません。しかし、それも役に立ちません。ヒントについて:実装が変更された場合(つまり、プライベート実装をスワップアウトしたい場合)、テストスイートが邪魔になるため、削除します(失敗するはずのテストが失敗するはずです)。
Matt Messersmith、2018年

13

単体テストの主な目的の1つがプログラムの内部をリファクタリングし、その機能を壊していないことを確認できることを考えると、単体テストが次のような細かいレベルで動作する場合、逆効果になります。プログラムコードを変更した場合は、テストを書き直す必要があります。


なぜあなたの答えは反対票が投じられたのかわかりません。それは短く、要点があり、100%正解です。
デビッドアルノ

3
@DavidArno:プライベートメソッドのテストは、テストの細分性とはあまり関係がないためかもしれません。それは、実装の詳細へのカップリングと関係があります。
ロバートハーヴェイ

10

プライベートメソッドの単体テストを作成すると、単体テストが実装の詳細に関連付けられます。

単体テストでは、クラスの外面でのクラスの動作をテストする必要があります(パブリックAPIです)。ユニットテストはクラスの内部について何も知る必要はありません。クラスの実装の詳細に対する単体テストを記述することは、リファクタリングの時期になるとあなたの手を縛ります。これらのテストは安定したAPIの一部ではないため、リファクタリングはほぼ確実にこれらのテストを中断させます。

いえ、プライベートメソッドの単体テストを作成する理由何でしょうか。

単体テストと段階的な開発の間には自然な緊張があります。REPL(read-eval-print loop)を使用するソフトウェア開発者は、クラスまたは関数を「成長」させながら、機能の小さなビットをすばやく記述してテストすることの生産性を証明できます。単体テスト主導の環境でそれを行う唯一の良い方法は、プライベートメソッドの単体テストを記述することですが、そのためには多くの摩擦があります。ユニットテストの記述には時間がかかり、テストする実際のメソッドが必要です。また、テストフレームワークは、メソッドを非公開にして外部APIを汚染しないようにする機能をサポートする必要があります。

C#や.NETなどの一部のエコシステムには、REPLに似た環境を作成する方法があります(Linqpadなどのツールでこれを実行します)が、プロジェクトにアクセスできないため、ユーティリティは制限されています。Visual Studioのイミディエイトウィンドウは不便です。それでも完全なIntellisenseはまだありません。完全修飾名を使用する必要があり、使用するたびにビルドがトリガーされます。


4
@ user949300 真のスコットランド人の誤解に陥らずにこれを議論するのは少し難しいですが、あらゆる種類の多くの不適切に書かれたテストがあります。ユニットテストの観点からは、内部実装の詳細を知らなくても、メソッドのパブリックコントラクトをテストする必要があります。特定の依存関係がX回呼び出されたと主張することは常に間違っているわけではありません。これが意味をなす状況があります。これは、テスト対象のユニットの契約で実際に伝えたい情報であることを確認する必要があります。
Vincent Savard、

3
@DavidArno:[肩をすくめる] 私はしばらくこれをやっています。プライベートメソッドの単体テストは、Microsoftがテストフレームワークでプロキシオブジェクトのサポートを停止することを決定するまで、常に問題なく機能しました。その結果、爆発はありませんでした。プライベートメソッドのテストを作成して、宇宙の穴を開けたことはありません。
ロバートハーヴェイ

2
@DavidArno:正当な理由を提供せずにインターネット上の誰かが悪い考えを言っているからといって、なぜ私に利益をもたらす完全に優れた手法の使用をやめるのですか?
ロバートハーヴェイ

2
単体テストから得られる主な利点は、コードをいじる「セーフティネット」を提供し、自分の変更がリグレッションを引き起こしていないことを確信できることです。そのため、プライベートヘルパーメソッドをテストすると、そのようなリグレッションを見つけやすくなります。プライベートヘルパーメソッドをリファクタリングして論理エラーを導入すると、そのプライベートメソッドに固有のテストが中断されます。私の単体テストがより一般的で、そのコードのユニットのインターフェースのみをテストした場合、問題は見つけにくくなります。
アレクサンダー-モニカを復活

2
@Fraxもちろん可能ですが、そのロジックにより、システム全体の統合テストを支持して、ユニットテストを無視する必要があります。結局のところ、「ほとんどの場合、同じ動作をテストするためにこれらのテストを変更できるはずです」
Alexander-Reinstate Monica

6

私の経験から、内部クラス、メソッドを単体テストすることは、通常、テストした関数、クラスを取り出さなければならないことを意味します。別の抽象化レベルを作成する。

これにより、単一責任原則の遵守が向上します。


これは受け入れられる答えになるはずです。
Jack Aidley、2018年

0

カバレッジのテストで一般的な問題が明らかになるため、これは良い質問だと思います。ただし、理論的にはプライベートメソッドを単体テストすることはできないはずなので、適切な答えを出すと、質問が正確ではないことがわかります。それが彼らが非公開である理由です。

おそらく、「プライベートメソッドをテストしたい場合はどうすればよいですか?」、そして答えはある程度明白です:テストを可能にする方法でそれらを公開する必要があります。さて、これは必ずしもメソッドを公開する必要があるという意味ではなく、それだけです。ほとんどの場合、より高度な抽象化を行う必要があります。別のライブラリまたはAPIに移動して、メインのAPIでその機能を公開せずに、そのライブラリでテストを実行できるようにします。

メソッドのアクセシビリティレベルが異なるのには理由があることを忘れないでください。最終的には、クラスがどのように使用されるのかを常に考える必要があります。



私は私の質問でこれに対処しようとしましたが、Pythonにはプライバシーの真の概念がなく、隠されたメソッドは単にリストされていませんが、名前がわかっている場合に使用できます
Serge Ballesta
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.