web-dev-qa-db-ja.com

junit&Java:非パブリックメソッドのテスト

JUnitは、クラス内のパブリックなメソッドのみをテストします。保護されていない(つまり、プライベート、保護されている)ものに対してjunitテストを実行するにはどうすればよいですか?

Junitを使用しないでテストすることはできますが、junitの標準メソッドは何であるかと思いまして。

98
jbu

ユニットテストについてのある考えでは、パブリックAPIのユニットテストのみを行うべきであり、そうすることで、非パブリックメソッドのコードをカバーする必要があるため、パブリックメソッドのみをテストできるようにする必要があるとしています。あなたのマイレージは異なる場合があります;私はこれが時々そうであり、時にはそうではないことを発見しました。

そうは言っても、非公開メソッドをテストするにはいくつかの方法があります。

  • 単体テストをテスト対象のクラスと同じパッケージに入れることで、保護されたメソッドとパッケージスコープメソッドをテストできます。これはかなり一般的な方法です。
  • パブリックとしてテストするメソッドをオーバーライドするテスト対象クラスのサブクラスを作成し、それらのオーバーライドされたメソッドがsuperキーワードで元のメソッドを呼び出すようにすることで、別のパッケージの単体テストから保護されたメソッドをテストできます。通常、この「テストサブクラス」は、テストを実行するJUnit TestCaseクラスの内部クラスになります。私の意見では、これはもう少しハックですが、やったことがあります。

お役に立てれば。

91
MattK

多くのユニットテストの問題と同様に、プライベートメソッドのテストは、実際には偽装の設計上の問題です。プライベートメソッドをテストするためにトリッキーなことをしようとするのではなく、プライベートメソッドのテストを書きたいと思うとき、「パブリックメソッドを通して徹底的にテストできるように、これをどのように設計する必要がありますか?」

それが機能しない場合、JUnitXはプライベートメソッドのテストを許可しますが、JUnit 3.8でのみ使用できると思います。

37
Kent Beck

JUnitテストを作成するとき、「今は自分のクラスのクライアントです」という微妙な心の転換を行う必要があります。つまり、プライベートはプライベートであり、クライアントが見る動作のみをテストします。

メソッドが本当にプライベートである必要がある場合、テストのためだけに可視化することは設計上の欠陥と考えます。クライアントが見るものに基づいて正しい動作を推測できるようにしなければなりません。

最初にこれを書いてから3年が経ち、Java反射.

汚い小さな秘密は、リフレクションを使用して、パブリックメソッドと同じようにJUnitでプライベートメソッドをテストできることです。心のこもった内容をテストしても、クライアントに公開されないようにすることができます。

26
duffymo

最も簡単な解決策は、JUnitテストを同じパッケージ(ただし、異なるディレクトリ)に配置し、メソッドにデフォルト(つまり、パッケージプライベート)の可視性を使用することです。

別のより複雑なアプローチは、リフレクションを使用してプライベートメソッドにアクセスすることです。

9
starblue

比較的少数の「パブリック」エントリポイントにかなりの量のロジックが埋め込まれている場合、おそらく 単一責任原則 に違反していることになります。可能であれば、コードを複数のクラスにリファクタリングし、最終的にはテスト対象の「パブリック」メソッドを増やす必要があります。

9
marcumka

ここに、「おそらくこの方法でやるべきではない」方法があります。ただし、この方法で行う理由があるのは確かに可能性の範囲内だと思います。次のコードはプライベートフィールドにアクセスしますが、プライベートメソッドのコードはほぼ同じです。

public void testPrivateField() throws InterruptedException {
    Class<ClassWPrivateField> clazz = ClassWPrivateField.class;
    try {
        Field privateField = clazz.getDeclaredField("nameOfPrivateField");
        privateField.setAccessible(true); // This is the line
        // do stuff
    } catch(NoSuchFieldException nsfe) {
        nsfe.printStackTrace();
        fail();
    } catch(IllegalAccessException iae) {
        iae.printStackTrace();
        fail();
    }

}
8
Cheese Daneish

ほとんどの場合、JavaプロジェクトでSpringを使用しているため、オブジェクトは依存性注入用に構築されています。アプリケーションコンテキスト内で組み立てられるパブリックインターフェイスのかなりきめ細かい実装になりがちです。このように、クラス自体は問題にならないほど小さいため、プライベートメソッドをテストする必要はほとんどありません。

Springを使用しない場合でも、小さくてシンプルなオブジェクトを大きく抽象化する同じプラクティスを採用する傾向があります。それぞれの抽象化は比較的シンプルですが、集約されたオブジェクトによって複雑になります。

私の経験では、プライベートメソッドを単体テストする必要があるということは、あなたが何を探しているのかを単純化できることを示しています。

それでも、あなたが本当に必要性を感じているなら:

  • 保護されたメソッドは、サブクラスでテストできます。
  • パッケージプライベートメソッドは、ユニットテストを同じパッケージに入れることでテストできます。そして
  • プライベートメソッドは、たとえば、パッケージプライベートファクトリプロキシメソッドを提供することにより、ユニットテストできます。理想的ではありませんが、プライベートはプライベートを意味します。
3
cletus

プライベートメソッドは(通常)別のパブリックメソッドを介して間接的にしかテストできないため、通常はプライベートメソッドをテストしません。運転をテストしてプライベートメソッドを作成する場合、それらは通常「抽出メソッド」リファクタリングの結果であり、すでに間接的にテストされています。

多くのロジックを持つプライベートメソッドのテストが心配な場合は、そのコードをパブリックメソッドの別のクラスに移動するのが最も賢明です。これを行うと、このコードを使用した以前の方法では、スタブまたはモックによって提供される機能を使用することでテストを簡素化できます。

3
Spoike

DP4jジャー

プライベートメソッドをテストするには、リフレクションとすべての回答で指摘されたものを使用する必要があります。

さて、このタスクは Dp4j jarを使用して簡素化されています。

  • Dp4jはコードを分析し、Reflection APIコードを自動的に生成します。

  • Dp4j.jarをCLASSPATHに追加するだけです。

  • Dp4j.jarには注釈プロセッサが含まれており、@ Test JUnit注釈が付けられたコード内のメソッドを探します。

  • Dp4jはこれらのメソッドのコードを分析し、プライベートメソッドに不正にアクセスしていることが検出されると、無効なプライベートメソッド参照をJavaのReflection APIを使用する同等のコードに置き換えます。

詳細を取得 こちら

2
VdeX

Andy Huntから思考を借りるには、プライベートメソッドでも、興味のある副作用が必要です。つまり、パブリックメソッドから呼び出され、オブジェクトの状態を変更する興味深いタスクを実行する必要があります。その状態変化をテストします。

パブリックメソッドpubMethodとプライベートメソッドprivMethodがあるとします。 pubMethodを呼び出すと、privMethodを呼び出してタスクを実行します(おそらく文字列を解析します)。次に、pubMethodはこの解析された文字列を使用して、何らかの方法でメンバー変数の値を設定するか、独自の戻り値に影響を与えます。 pubMethodの戻り値またはメンバー変数に対する望ましい効果を監視してテストします(おそらくアクセサーを使用して取得します)。

2
Wil Doane

私は同じ問題に出くわしましたが、「プライベートにする必要がある場合は、おそらくリファクタリングする必要があります」とは思いません。

クラスの内部で何らかの方法で分離したい機能があるとします。たとえば、次のようなものがあるとします。

public class HolderOfSomeStrings{

    private List<String> internal_values;

    public List<String> get()
    {
       List<String> out = new ArrayList<String>();
       for (String s:internal_values)
       { 
           out.add(process(s));
       }
      return get;
    }

   private static String process(String input)
   {
     //do something complicated here that other classes shouldn't be interested in
    }

}

ここでのポイントは、junitがプロセスをパブリックにするか、少なくとも保護するか、独自のユーティリティクラスに入れることを強制することです。しかし、HolderOfSomeStringsの内部​​ロジックのようなものである場合、これが正しいことはまったく明確ではありません。これはプライベートである必要があり、より目に見えるようにすると、何らかの形でコードが使い果たされるように思えます。

2
Steve B.

上記のようにリフレクションを使用して、プライベートメソッドをテストします。 TDDをフォローしている場合、TDDが後で驚くことはないことを示唆しているのであれば、プライベートメソッドをテストする必要があります。したがって、プライベートをテストするためのパブリックメソッドを完了するのを待つべきではありません。これは、リファクタリング時のより詳細な回帰テストに役立ちます。

1

JUnitの代わりにTestNGを使用できます。JUnitはメソッドがプライベートまたはパブリックであることを気にしません。

1
Pierre Gardin

@duffymoには、クライアントの観点からコードをテストする必要があることに絶対に同意します(彼はこのように考えることをやめたと言いますが)。ただし、この観点から、プライベートと他の意味は異なります。プライベートメソッドのクライアントはクラス自体なので、外部(パブリック/パッケージ保護)APIを介してテストすることを好みます。ただし、保護されたメンバーおよびパッケージで保護されたメンバーは外部クライアント用に存在するため、所有クラスを継承する偽物または同じパッケージに存在する偽物でそれらをテストします。

0
Cagatay Kalan

ほぼすべての投稿に同意します。おそらく、リファクタリングする必要があります。おそらく、パブリックを除いてプライベートをテストするべきではありません。

クラス自体をメソッドではなく「ユニット」と考えてください。クラスをテストしており、パブリックメソッドの呼び出し方法に関係なく、クラスが有効な状態を維持できることを確認しています。

プライベートメソッドを呼び出すと、カプセル化が破壊され、テストが実際に無効になります。

0
Bill K

「PrivateAccessor.invoke」を探します。私のコードは「junitx.util」からそれをインポートしますが、どこから来たのかわかりません。

0
Paul Tomblin

これは一種の派生物であり、「ポリグロットプログラミング」の問題全体がどこで解決するのかわかりませんが、GroovyテストはJunitで実行可能であり、パブリック/非パブリックの問題全体を無視します。補足説明として、公式には「バグ」として分類されていましたが、修正しようとすると、元の状態に戻されるほどの嵐が発生しました。

0
mezmo