プライベートメソッドを公開して単体テストを行うと…いい考えですか?


301

司会者注: ここにはすでに39の回答が投稿されています(一部は削除されています)。回答 を投稿する前に、ディスカッションに意味のあるものを追加できるかどうかを検討してください。あなたはたぶん他の誰かがすでに言ったことを繰り返すだけです。


ユニットテストを記述するためだけに、クラスのプライベートメソッドをパブリックにする必要がある場合があります。

通常これは、メソッドにクラス内の他のメソッド間で共有されるロジックが含まれていて、独自にロジックをテストするほうが簡単なためです。または、スレッドの問題を心配せずに同期スレッドで使用されるロジックをテストしたいという別の理由も考えられます。 。

私は本当にそれが好きではないので、他の人が自分でこれをやっているのを見つけますか?個人的には、ボーナスは、クラスの外では実際にサービスを提供しないメソッドを公開することの問題を上回ると思います...

更新

皆様のご回答ありがとうございます、人々の興味をそそられたようです。これはクラスが使用される唯一の方法であり、私はこれに同意するため、一般的なコンセンサスはパブリックAPIを介してテストを行うべきであると考えています。私が上記でこれを実行する2つのケースはまれなケースであり、それを実行することのメリットはそれだけの価値があると思いました。

しかし、誰もがそれが実際に起こるべきではないという指摘を見ることができます。そして、それについてもう少し考えると、テストに対応するためにコードを変更することは悪い考えだと思います。結局、テストはある意味でサポートツールであり、システムを「サポートツールをサポートする」ように変更するのは露骨です。悪い習慣。


2
「システムを「サポートツールをサポートする」ように変更するのは、露骨に悪い習慣です。」ええ、そうです。しかし、大藤は、あなたのシステムが確立ツールでうまく動作しない場合は、多分何かがあるシステムと間違っ。
ティロ2011


1
答えではありませんが、リフレクションによっていつでもそれらにアクセスできます。
Zano 2015

33
いいえ、公開するべきではありません。代わりに、クラスの動作を、そのパブリックAPIを介してテストする必要があります。そして、内部を公開することが本当に唯一のオプションである場合(私は疑います)、少なくともアクセサーパッケージを保護して、同じパッケージ内のクラス(テストがそうであるように)だけがそれらにアクセスできるようにする必要があります。
JBニゼット2015

4
はい、そうです。単体テストからアクセスできるようにするために、メソッドにpackage-private(デフォルト)の可視性を与えることは完全に許容されます。Guavaのようなライブラリは、@VisibileForTestingそのようなメソッドを作成するための注釈を提供しprivateます。メソッドが存在しない理由が適切に文書化されるように、これを行うことをお勧めします。
ボリスザスパイダー

回答:


186

注:
この回答は、もともと質問のために投稿されました。ゲッターを介してプライベートインスタンス変数を公開するのに、単体テストだけで十分な理由はありますか?これはこれにマージされたので、そこに提示されたユースケースに固有の少しかもしれません。

一般的なステートメントとして、私は通常、テストを容易にするために「プロダクション」コードをリファクタリングするすべての人です。しかし、それはここで良い電話になるとは思いません。(通常)優れた単体テストでは、クラスの実装の詳細については気にせず、目に見える動作についてのみ気にする必要があります。内部スタックをテストに公開する代わりに、first()またはを呼び出した後に、クラスが期待どおりの順序でページを返すことをテストできますlast()

たとえば、次の疑似コードを考えてみます。

public class NavigationTest {
    private Navigation nav;

    @Before
    public void setUp() {
        // Set up nav so the order is page1->page2->page3 and
        // we've moved back to page2
        nav = ...;
    }

    @Test
    public void testFirst() {
        nav.first();

        assertEquals("page1", nav.getPage());

        nav.next();
        assertEquals("page2", nav.getPage());

        nav.next();
        assertEquals("page3", nav.getPage());
    }

    @Test
    public void testLast() {
        nav.last();

        assertEquals("page3", nav.getPage());

        nav.previous();
        assertEquals("page2", nav.getPage());

        nav.previous();
        assertEquals("page1", nav.getPage());
    }
}

8
私はあなたの答えに完全に同意します。多くの開発者は、デフォルトのアクセス修飾子を使用して、優れたオープンソースフレームワークがこの戦略を使用することを正当化するように誘惑され、それが正しい必要があります。できるからといって、そうすべきだとは限らない。+1
2015

6
他の人が有用な洞察を提供してくれましたが、私はこれがこの問題に最も適切であると思う解決策だと言わざるを得ません。これにより、意図した動作が内部でどのように達成されるかについて、テストに完全にとらわれません。+10!
Gitahi Ng'ang'a 2015

ちなみに、ここで@Mureinikが提供する例では、テストメソッドが実際のテスト対象メソッドよりも多くカバーすることになりますか?たとえば、testFirst()はnext()を呼び出しますが、これは実際にはテスト対象のメソッドではなく、first()です。4つのナビゲーションメソッドすべてをカバーする1つのテストメソッドだけでは、明確で明確ではないでしょうか?
Gitahi Ng'ang'a

1
これは理想的ですが、コンポーネントが成功しただけでなく、正しい方法でコンポーネントを実行したかどうかを確認する必要がある場合があります。これはブラックボックステストが困難な場合があります。コンポーネントをホワイトボックステストする必要がある場合があります。
Peter Lawrey、2015

1
単体テストのためだけにゲッターを作成しますか?これは、インターフェイスがデータではなく動作を公開する必要があるという点で、OOP /オブジェクト思考の粒度に間違いなく反しています。
IntelliData 2017年

151

個人的には、私はパブリックAPIを使用して単体テストをしたいのですが、テストを簡単にするためだけにプライベートメソッドをパブリックにすることは決してありません。

プライベートメソッドを単独でテストする場合は、JavaでEasymock / Powermockを使用してこれを実行できます。

あなたはそれについて実際的である必要があり、また物事がテストするのが難しい理由を知っておくべきです。

' テストに耳を傾ける '-テストが難しい場合、それはあなたのデザインについて何か教えていますか?このメソッドのテストが簡単で、パブリックAPIによるテストで簡単にカバーできる場所にリファクタリングできますか?

これは、「レガシーコードを効果的に使用する」でMichael Feathersが言っていることです。

「多くの人がこの問題を回避する方法を理解するために多くの時間を費やしています...本当の答えは、プライベートメソッドをテストしたいのであれば、メソッドをプライベートにするべきではないことです。メソッドをパブリックにする場合おそらくあなたを困らせます、それはそれが別の責任の一部であるからです;それは別のクラスにあるべきです。」[ M.フェザーズによるレガシーコード(2005)の効果的な使用 ]


5
Easymock / Powermockの+1、およびプライベートメソッドを公開しない
LeonardBrünings2011

51
+1「テストを聞く」の場合。プライベートメソッドをテストする必要があると感じた場合、そのメソッドのロジックが非常に複雑であるため、別のヘルパークラスに配置する必要がある可能性があります。次に、ヘルパークラスをテストできます。
Phil

2
「テストを聞く」がダウンしているようです。:ここでは、キャッシュされたリンクですwebcache.googleusercontent.com/...
ジェステルフォード

1
私は@Phil(特に本のリファレンス)に同意しますが、レガシーコードで鶏/卵の状況に陥ることがよくあります。私はこのメソッドが別のクラスに属していることを知っていますが、そもそもテストなしでは100%快適なリファクタリングではありません。
ケビン

私は医者に同意します。プライベートメソッドをテストする必要がある場合は、おそらく別のクラスにあるはずです。プライベートメソッドがパブリック/保護される可能性が高い他のクラス/ヘルパークラスに注意してください。
rupweb 2017年

67

他の人が言ったように、プライベートメソッドをユニットテストすることは少し疑わしいです。プライベート実装の詳細ではなく、パブリックインターフェイスを単体テストします。

つまり、C#でプライベートなものをユニットテストするときに使用する手法は、アクセシビリティ保護をプライベートから内部にダウングレードし、ユニットテストアセンブリをInternalsVisibleToを使用してフレンドアセンブリとしてマークすることです。ユニットテストアセンブリは内部をパブリックとして扱うことが許可されますが、誤ってパブリックサーフェスに追加することを心配する必要はありません。


私もこの手法を使用していますが、通常はレガシーコードの最後の手段として使用します。プライベートコードをテストする必要がある場合、不足しているクラスがあるか、テスト対象のクラスが実行していることが多すぎます。上記のコードを、個別にテストできる新しいクラスに抽出します。このようなトリックはほとんど使用しないでください。
Finglas、2011

ただし、クラスを抽出してそれだけをテストすると、元のクラスが実際に抽出されたコードを使用しているという知識がテストから失われます。テストでこのギャップをどのように埋めることをお勧めしますか?答えが、抽出されたロジックを使用する方法で元のクラスのパブリックインターフェイスをテストするだけの場合は、そもそもなぜそれを抽出する必要があるのでしょうか。(私はここで対立するように聞こえることを意味していません、ところで-私はいつも人々がそのようなトレードオフのバランスをとる方法を常に疑問に思っていました。)
マルロス

@malコラボレーションテストを実施します。つまり、新しいクラスが正しく呼び出されたことを確認するためのモックです。以下の私の答えを参照してください。
Finglas、2011

テストしたい内部ロジックを持つクラスから継承するテストクラスを記述して、内部ロジックのテストに使用するパブリックメソッドを提供できますか?
ショルシンガー、2011

1
@sholsinger:C#では、パブリッククラスは内部クラスから継承しない場合があります。
Eric Lippert、2011

45

多くの回答はパブリックインターフェイスのテストのみを提案していますが、私見ではこれは非現実的です。メソッドが5つのステップを実行する場合、これらの5つのステップをすべてではなく個別にテストする必要があります。これは、すべての5つの方法、テストする必要があります(テスト用以外)そうかもしれませんがprivate

「プライベート」メソッドをテストする通常の方法は、すべてのクラスに独自のインターフェースを提供し、「プライベート」メソッドを作成するpublicことですが、インターフェースには含めません。このように、それらはまだテストできますが、インターフェースを膨らませません。

はい、これはファイルおよびクラス膨張の結果になります。

はい。これにより、publicおよびprivate指定子が冗長になります。

はい、これはお尻の痛みです。

これは、残念ながら、コードをテスト可能にするために私たちが行う多くの犠牲の 1つです。おそらく、将来の言語(またはC#/ Javaの将来のバージョン)には、クラスおよびモジュールのテストをより便利にする機能が含まれるでしょう。しかし、その間、これらのフープをジャンプする必要があります。


それらの各ステップは独自のクラスである必要があると主張する人もいますが、私は同意しません。それらすべてが状態を共有する場合、5つのメソッドが行う5つの別個のクラスを作成する理由はありません。さらに悪いことに、これはファイルとクラスの膨張につながります。 さらに、モジュールのパブリックAPIに感染します。これらのクラスはすべてpublic、別のモジュールからテストする場合(または、同じモジュールにテストコードを含める場合、つまり、テストコードを製品に同梱する場合)でなければなりません。


2
前半のいい答え
cmcginty

7
+1:私にとって最良の答え。自動車メーカーが同じ理由でその車の一部をテストしない場合、誰もがそれを購入するかどうかはわかりません。コードをパブリックに変更する必要がある場合でも、コードの重要な部分はテストする必要があります。
泰-宋新

1
非常に良い答え。私の温かい投票があります。回答を追加しました。興味があるかもしれません。
davidxxx 2017

29

ユニットテストでは、パブリックコントラクトをテストする必要があります。これは、クラスをコードの他の部分で使用する唯一の方法です。プライベートメソッドは実装の詳細です。パブリックAPIが正しく機能する限り、テストしないでください。実装は問題ではなく、テストケースを変更せずに変更できます。



フェア、私がテストケースでプライベートを使用することを決定したまれな状況は、オブジェクトの正当性を検証しているときでした-オブジェクトがすべてのJNIハンドラーを閉じている場合 当然、公開APIとして公開されていませんが、発生しない場合はメモリリークが発生します。しかし、面白いことに、パブリックAPIの一部としてメモリリークディテクターを導入した後、私はそれも後で取り除きました。
kan

11
これは欠陥のあるロジックです。単体テストのポイントは、後でエラーを見つけることです。プライベートメソッドをテストしないことにより、テストのギャップを残し、それがずっと後まで発見されない可能性があります。プライベートメソッドが複雑で、パブリックインターフェイスで簡単に実行できない場合は、単体テストする必要があります。
cmcginty 2011

6
@Casey:プライベートメソッドがパブリックインターフェイスによって実行されない場合、なぜそこにあるのですか?
ティロ2011

2
@DaSilva_Ireland:今後、テストケースを維持することは困難になります。実際のクラスの唯一のユースケースは、パブリックメソッドを介したものです。つまり、他のすべてのコードは、パブリックAPIを介してのみクラスを使用できます。したがって、実装を変更してプライベートメソッドのテストケースが失敗した場合でも、何かが間違っているというわけではありません。さらに、プライベートのみをテストしていて、パブリックを完全にテストしていない場合、潜在的なバグはカバーされません。 。理想的には、テストケースはすべての可能なケースをカバーし、クラスの実際の使用のケースのみをカバーする必要があります。
kan

22

IMO、クラスを内部でどのように実装するかについて深い想定をしないテストを書く必要があります。おそらく、別の内部モデルを使用して後でリファクタリングしたいと思うかもしれませんが、それでも以前の実装と同じ保証を行います。

このことを念頭に置いて、クラスが現在どのような内部実装を持っているかに関係なく、契約がまだ成立していることをテストすることに集中することをお勧めします。パブリックAPIのプロパティベースのテスト。


6
コードのリファクタリング後に書き換える必要のない単体テストの方が優れていることに同意します。ユニットテストの書き換えに時間を費やすだけではありません。さらに重要な理由は、ユニットテストの最も重要な目的は、リファクタリング後もコードが正しく動作することであると私が考えるためです。リファクタリング後にすべての単体テストを書き直す必要がある場合は、単体テストのメリットを失うことになります。
kasperd 2015

20

パッケージをプライベートにするのはどうですか?そうすれば、テストコードでそれ(およびパッケージ内の他のクラス)を見ることができますが、それでもユーザーには表示されません。

しかし、実際には、プライベートメソッドをテストするべきではありません。これらは実装の詳細であり、契約の一部ではありません。彼らが行うすべてのことは、パブリックメソッドを呼び出すことでカバーする必要があります(パブリックメソッドによって実行されないコードがコードに含まれている場合は、それで問題ありません)。プライベートコードが複雑すぎる場合、クラスはおそらく多くのことを実行しており、リファクタリングを必要としています。

メソッドを公開することは大きなコミットメントです。これを実行すると、他の人がそれを使用できるようになり、変更するだけではできなくなります。


あなたの陰部をテストする必要がある場合は+1、あなたはおそらく、クラスが欠落している
JKを。

不足しているクラスの+1。他の人がすぐに「内部をマークする」バンドワゴンにジャンプしないのを見てうれしい。
Finglas

少年私はパッケージのプライベートな答えを見つけるために長い道のりをたどる必要がありました。
ビルK

17

更新他の多くの場所で、この質問に対するより拡張されたより完全な回答を追加しました。これは私のブログで見つけることができます

それをテストするために何かを公開する必要がある場合、これは通常、テスト中のシステムが単一の責任の原則に従っていないことを示唆しています。したがって、導入する必要がある不足しているクラスがあります。コードを新しいクラスに抽出した後、それを公開します。これで簡単にテストでき、SRPをフォローしています。他のクラスは、コンポジションを介してこの新しいクラスを呼び出すだけです。

メソッドを公開する/テストアセンブリにコードを表示可能にするなどの言語トリックを使用することは、常に最後の手段です。

例えば:

public class SystemUnderTest
{
   public void DoStuff()
   {
      // Blah
      // Call Validate()
   }

   private void Validate()
   {
      // Several lines of complex code...
   }
}

バリデーターオブジェクトを導入してこれをリファクタリングします。

public class SystemUnderTest
{
    public void DoStuff()
    {
       // Blah
       validator.Invoke(..)
    }
}

バリデータが正しく呼び出されることをテストするだけです。検証の実際のプロセス(以前のプライベートロジック)は、純粋に分離してテストできます。この検証に合格するために複雑なテストを設定する必要はありません。


1
個人的には、SRPが長すぎる可能性があると思います。たとえば、アプリケーションでアカウントの更新/作成/削除を処理するアカウントサービスを作成する場合、操作ごとに個別のクラスを作成する必要があると言っていますか?たとえば、検証についてはどうでしょうか。検証ロジックを他のクラスで使用することがない場合は、それを別のクラスに抽出する価値がありますか?コードを読みにくく、簡潔にするだけでなく、アプリケーションに余分なクラスを詰め込むだけのimo!
jcvandan 2011

1
それはすべてシナリオに依存します。「あることをする」という人の定義は、他の人とは異なる場合があります。この場合、あなた自身にとって、あなたがテストしたいプライベートコードは複雑であり、設定するのが面倒です。あなたがそれをテストしたいという事実はそれが多すぎることを証明しています。したがって、はい、新しいオブジェクトを持つことが理想的です。
Finglas、2011

1
コードを読みにくくすることに関しては、私は強く反対します。検証用のクラスを用意すると、他のオブジェクトに検証を埋め込むよりも読みやすくなります。まだ同じ量のコードがあり、1つの大きなクラスにネストされていないだけです。私は1つの大きなクラスよりも、1つのことを非常にうまく行ういくつかのクラスが欲しいです。
Finglas、2011

4
@dormisherは、アカウントサービスに関して、SRPを「変更する単一の理由」と考えています。CRUD操作が自然に一緒に変化する場合、それらはSRPに適合します。通常は、データベーススキーマが変更される可能性があるため、これらを変更します。これは、挿入と更新に自然に影響します(ただし、常に削除されるとは限りません)。
Anthony Pegram 2011

あなたはこれについてどう思うか@finglas:stackoverflow.com/questions/29354729/...。私はプライベートメソッドをテストしたくないので、クラスに分けるべきではないかもしれませんが、他の2つのパブリックメソッドで使用されているため、同じロジックを2回テストすることになります。
Rodrigo Ruiz

13

いくつかの素晴らしい答え。私が言及しなかったことの1つは、テスト駆動開発(TDD)では、リファクタリングフェーズ中にプライベートメソッドが作成され(リファクタリングパターンの例については、メソッドの抽出を参照)、したがって、必要なテストカバレッジがすでにあるはずです。 。正しく行われた場合(そしてもちろん、正しさに関してはさまざまな意見が寄せられることになります)、テストできるようにプライベートメソッドをパブリックにする必要がないか心配する必要はありません。


11

スタック管理アルゴリズムをユーティリティクラスに分割してみませんか?ユーティリティクラスはスタックを管理し、パブリックアクセサーを提供できます。その単体テストは、実装の詳細に焦点を当てることができます。アルゴリズム的にトリッキーなクラスの詳細なテストは、エッジケースを見つけ出し、カバレッジを確保するのに非常に役立ちます。

次に、現在のクラスは、実装の詳細を公開することなく、ユーティリティクラスに明確に委任できます。他の人が推奨しているように、そのテストはページ分割の要件に関連しています。


10

Javaでは、パッケージをプライベートにする(つまり、可視性修飾子をオフにする)オプションもあります。ユニットテストがテスト対象のクラスと同じパッケージにある場合、これらのメソッドを確認でき、メソッドを完全にパブリックにするよりも少し安全です。


10

通常、プライベートメソッドは「ヘルパー」メソッドとして使用されます。したがって、これらは基本的な値のみを返し、オブジェクトの特定のインスタンスを操作することはありません。

テストする場合は、いくつかのオプションがあります。

  • リフレクションを使用する
  • メソッドにパッケージアクセスを付与する

または、新しいクラスの候補として十分な場合は、ヘルパーメソッドをパブリックメソッドとして使用して新しいクラスを作成することもできます。

これについては非常に良い記事があります。


1
公正なコメント。単なる選択肢でしたが、おそらく悪いものでした。
adamjmarkham 2011

@Finglasリフレクションを使用してプライベートコードをテストすることが悪い考えである理由
Anshul 2014年

@Anshulプライベートメソッドは実装の詳細であり、動作ではありません。つまり、変化します。名前が変わります。パラメータが変更されます。内容が変わります。つまり、これらのテストは常に失敗します。一方、パブリックメソッドは、APIの面ではるかに安定しているため、より回復力があります。パブリックメソッドでのテストを提供すると、実装の詳細ではなく、動作がチェックされます。
Finglas、2014年

1
@Finglas実装の詳細をテストしたくないのはなぜですか?動作する必要があるのはコードであり、それが頻繁に変更されると、より頻繁に破損することが予想されます。これは、パブリックAPIよりもテストする理由です。コードベースが安定したら、将来誰かがやって来て、クラスの内部動作を壊すプライベートメソッドを変更するとします。クラスの内部をテストするテストは、何かが壊れていることをすぐに彼らに伝えます。はい、それはあなたのテストを維持することを難しくしますが、それはあなたが完全なカバレッジテストから期待するようになるものです。
Anshul 2014年

1
@Finglasテストの実装の詳細に問題はありません。それがあなたのスタンスですが、これは広く議論されているトピックです。内部をテストすると、完全なカバレッジには必要ないかもしれませんが、いくつかの利点があります。パブリックAPIを経由するのではなく、プライベートメソッドを直接テストしているため、テストの焦点が絞られ、テストが混乱する可能性があります。プライベートメンバーを手動で無効にして、矛盾する内部状態に応じてパブリックAPIが適切なエラーを返すことを確認できるため、ネガティブテストがより簡単になります。他にも例があります。
Anshul 2014年

9

C#を使用している場合は、メソッドを内部にすることができます。そうすれば、パブリックAPIを汚染することはありません。

次に、dllに属性を追加します

[アセンブリ:InternalsVisibleTo( "MyTestAssembly")]

これで、すべてのメソッドがMyTestAssemblyプロジェクトに表示されます。多分完璧ではないかもしれませんが、それをテストするためだけにプライベートメソッドを公開する方が良いでしょう。


7

必要に応じて、リフレクションを使用してプライベート変数にアクセスします。

ただし、実際には、クラスの内部状態は気にせず、パブリックメソッドが予想できる状況で期待どおりの結果を返すことをテストしたいだけです。


voidメソッドで何をしますか?
IntelliData 2017年

@IntelliData必要に応じて、リフレクションを使用してプライベート変数にアクセスします。
Daniel Alexiuc 2017年

6

あなたが何らかの利益を得て、将来的に問題が発生する可能性があるかどうかはわかりません。呼び出しのコントラクトを変更する場合、プライベートメソッドをテストするだけで、クラスの使用方法をテストするのではなく、意図したことのない人工的なシナリオを作成します。

さらに、メソッドをパブリックとして宣言すると、6か月後に(メソッドをパブリックにする唯一の理由はテストのためであることを忘れてから)、あなた(またはプロジェクトを引き渡した場合)に完全に異なる誰かが勝った使用しないでください。意図しない結果やメンテナンスの悪夢につながる可能性があります。


1
クラスがサービスコントラクトの実装であり、それがそのインターフェイスを介してのみ使用される場合-この方法では、プライベートメソッドが公開されていても、表示されないので呼び出されません
jcvandan

それでも、クラスをリファクタリングして内部メソッドを分割する場合は、テストを変更する必要があるため、テストを変更する必要があります。新しいテストが古いテストと同じように機能することを保証する努力。
beny23 2011

また、テストカバレッジがプライベートメソッド内のすべてのエッジケースを確実に含む唯一の方法がそれを直接呼び出すことである場合、それは、クラスの複雑さがそれを簡略化するためのリファクタリングを必要とすることを示している可能性があります。
beny23 2011

@dormisher-クラスがインターフェースを介してのみ使用される場合は、パブリックインターフェースメソッドが正しく動作することをテストするだけで済みます。パブリックメソッドが意図したとおりに機能する場合、なぜプライベートメソッドを気にするのですか?
Andrzej Doyle

@Andrzej-私は本当にそうすべきではないと思いますが、それが有効であると私が信じる状況があります。たとえば、モデルの状態を検証するプライベートメソッド、このようなメソッドをテストする価値があるかもしれませんが、このようなメソッドは、とにかくパブリックインターフェイス
jcvandan '17

6

単体テストに関しては、メソッドを追加しないでください。first()各テストの前に呼び出されるメソッドについてのみテストケースを作成する方がよいと思います。あなたは複数回呼び出すことができます- next()previous()およびlast()結果があなたの期待に一致するかどうかを確認します。(テストの目的でのみ)クラスにメソッドを追加しない場合、テストの「ブラックボックス」の原則に固執すると思います。



5

更新では、パブリックAPIを使用してテストするだけでよいと述べています。ここには実際には2つの学校があります。

  1. ブラックボックステスト

    ブラックボックススクールによると、クラスはブラックボックスと見なされ、内部の実装は誰にも見えません。これをテストする唯一の方法は、パブリックAPIを使用することです。これは、クラスのユーザーが使用するのと同じです。

  2. ホワイトボックステスト。

    ホワイトボックススクールは、クラスの実装に関する知識を自然に使用し、クラスをテストして、期待どおりに機能することを確認することを自然と考えています。

私はその議論に賛成できない。クラス(またはライブラリなど)をテストするには2つの異なる方法があることを知っておくことは興味深いと思いました。


4

実際にこれを行う必要がある状況があります(たとえば、いくつかの複雑なアルゴリズムを実装している場合)。package-privateを実行するだけで十分です。しかし、ほとんどの場合、クラスが複雑すぎて、ロジックを他のクラスに分解する必要があります。


4

分離してテストするプライベートメソッドは、クラスに埋め込まれた別の「概念」があることを示しています。その「概念」を独自のクラスに抽出し、別個の「ユニット」としてテストします。

このビデオを見て、主題についての非常に興味深い見解を見てください。


4

テストにコードを指示させることは決してありません。私はTDDや他のDDについて話しているのではありません。アプリでこれらのメソッドを公開する必要がありますか?もしそうなら、それらをテストします。そうでない場合は、テストのためだけに公開しないでください。変数なども同様です。アプリケーションのニーズに応じてコードを指示し、テストでニーズが満たされているかどうかをテストします。(繰り返しますが、最初にテストすることを意味するのではなく、テストの目標を満たすためにクラス構造を変更することを意味します)。

代わりに、「より高いテスト」を行う必要があります。プライベートメソッドを呼び出すメソッドをテストします。ただし、テストではアプリケーションのニーズをテストする必要があり、「実装の決定」ではありません。

例(ここでの疑似コード)。

   public int books(int a) {
     return add(a, 2);
   }
   private int add(int a, int b) {
     return a+b;
   } 

「追加」をテストする理由はありません。代わりに「本」をテストできます。

テストにコード設計の決定をさせてはいけません。期待どおりの結果が得られるかどうかをテストする。


1
「テストにコード設計の決定をさせてはいけない」-私は同意しません。テストするのが難しいのはコードの匂いです。テストできない場合、壊れていないことをどうやって確認しますか?
アルフレッドアームストロング

明確にするために、たとえば、ライブラリの外部API設計を変更してはならないことに同意します。ただし、テストしやすくするには、リファクタリングが必要になる場合があります。
アルフレッドアームストロング

テストのためにリファクタリングする必要がある場合、それはあなたが心配すべきコードのにおいではないことを私は発見しました。他に何か問題があります。もちろん、それは個人的な経験であり、私たちはおそらく両方の方法で確実なデータを使って議論することができます。そもそも「デザインが悪い」ではない「テストするのが難しい」ということはありませんでした。
coteyr、2015

このソリューションは、本のような値を返すメソッドに適しています。アサートで戻り値の型を確認できます。しかし、どうやってvoidメソッドをチェックしますか?
IntelliData

voidメソッドは何かを行うか、存在する必要はありません。彼らがしていることを確認してください
coteyr

2

単体テストを実施することのボーナスは、一部のメンバーの可視性を高めるという問題を上回ることに同意する傾向があります。わずかな改善は、それを保護された仮想にすることであり、それを公開するためにテストクラスでオーバーライドすることです。

または、個別にテストしたい機能である場合、デザインから欠落しているオブジェクトを示唆していませんか?多分あなたはそれを別のテスト可能なクラスに置くことができます...そしてあなたの既存のクラスはこの新しいクラスのインスタンスに委譲するだけです。


2

私は通常、テストクラスをテスト中のクラスと同じプロジェクト/アセンブリに保持します。
この方法で必要なのはinternal、関数/クラスをテスト可能にするために可視性です。

これは、テストクラスを除外する必要があるビルドプロセスを多少複雑にします。すべてのテストクラスに名前を付けることでこれを実現しますTestedClassTest、正規表現を使用してこれらのクラスをフィルター処理います。

もちろん、これは質問のC#/ .NET部分にのみ適用されます


2

私は、多くの場合、何かのように呼ばれるメソッドを追加しますvalidateverifycheckオブジェクトの内部状態をテストするために呼び出すことができるように、クラスに、など、。

このメソッドは、リリース用にコンパイルされないように、ifdefブロック(主にC ++で記述)にラップされる場合があります。しかし、リリースでは、プログラムのオブジェクトツリーを調べて確認する検証メソッドを提供すると便利な場合があります。


2

Guavaには、スコープ(パッケージまたはパブリック)が拡大されているメソッドをマークするための@VisibleForTestingアノテーションがあります。同じことには@Privateアノテーションを使用します。

公開APIはテストする必要がありますが、通常は公開されないものにアクセスすることが便利で賢明な場合があります。

いつ:

  • クラスを複数のクラスに分割することで、クラスが完全に読みにくくなります。
  • よりテストしやすくするために
  • そして内部へのテストアクセスを提供することはトリックをするでしょう

宗教は工学を踏みにじっているようです。


2

通常、これらのメソッドはそのままにしてprotected、ユニットテストを同じパッケージ内(ただし、別のプロジェクトまたはソースフォルダー内)に配置します。クラスローダーが同じ名前空間内に配置するため、すべての保護されたメソッドにアクセスできます。


2

いいえ、その猫をスキニングするより良い方法があるので。

一部の単体テストハーネスは、クラス定義のマクロに依存しており、テストモードでビルドされると、自動的に展開してフックを作成します。非常にCスタイルですが、動作します。

より簡単なOOのイディオムは、「プライベート」ではなく「保護」をテストしたいものを作成することです。テストハーネスはテスト中のクラスを継承し、保護されたすべてのメンバーにアクセスできます。

または、「友達」オプションを選択します。個人的にこれはC ++の機能で、カプセル化の規則に違反しているので、私は少なくとも気に入っています。しかし、C ++がいくつかの機能を実装する方法にはたまたま必要です。

とにかく、ユニットテストを行っている場合は、それらのメンバーに値を挿入する必要がある可能性が高くなります。ホワイトボックスのテキストメッセージは完全に有効です。そして、それは本当にあなたのカプセル化壊すでしょう


2

.Netには、 PrivateObjectプライベートメソッドにアクセスできるように設計さます。

詳細については、MSDNまたはこちらのStack Overflowを参照してください。

(これまで誰も言及していなかったのではないかと思います。)

これでは不十分な場合もありますが、その場合はリフレクションを使用する必要があります。

それでも私はプライベートメソッドをテストしないという一般的な推奨事項を順守しますが、いつものように常に例外があります。


1

他のコメントで広く言及されているように、単体テストはパブリックAPIに焦点を当てるべきです。ただし、長所/短所と正当化は別として、リフレクションを使用して単体テストでプライベートメソッドを呼び出すことができます。もちろん、JREのセキュリティで許可されていることを確認する必要があります。プライベートメソッドの呼び出しは、Spring FrameworkがReflectionUtilsとともに使用するものです(「makeAccessible(Method)メソッドを)。

以下は、プライベートインスタンスメソッドを持つ小さなサンプルクラスです。

public class A {
    private void doSomething() {
        System.out.println("Doing something private.");
    }
}

そして、プライベートインスタンスメソッドを実行するサンプルクラス。

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class B {
    public static final void main(final String[] args) {
        try {
            Method doSomething = A.class.getDeclaredMethod("doSomething");
            A o = new A();
            //o.doSomething(); // Compile-time error!
            doSomething.setAccessible(true); // If this is not done, you get an IllegalAccessException!
            doSomething.invoke(o);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
}

Bを実行すると印刷さDoing something private. れます本当に必要な場合は、リフレクションをユニットテストで使用してプライベートインスタンスメソッドにアクセスできます。


0

個人的には、プライベートメソッドをテストするときに同じ問題があります。これは、一部のテストツールが制限されているためです。ツールがデザインではなくツールを変更するというニーズに応えられない場合、限られたツールによってデザインが動かされるのはよくありません。C#を求めるのは良いテストツールを提案することはできませんが、Javaには2つの強力なツール(TestNGとPowerMock)があり、対応する.NETプラットフォームのテストツールを見つけることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.