私はJavaプロジェクトに取り組んでいます。ユニットテストは初めてです。Javaクラスのプライベートメソッドを単体テストする最良の方法は何ですか?
私はJavaプロジェクトに取り組んでいます。ユニットテストは初めてです。Javaクラスのプライベートメソッドを単体テストする最良の方法は何ですか?
回答:
通常、プライベートメソッドを直接単体テストすることはありません。これらはプライベートなので、実装の詳細と考えてください。誰もそれらのいずれかを呼び出して、特定の方法で動作することを期待することはありません。
代わりに、パブリックインターフェイスをテストする必要があります。プライベートメソッドを呼び出すメソッドが期待どおりに機能している場合は、プライベートメソッドが正常に機能していると拡張機能で想定します。
一般的に、私はそれを避けます。プライベートメソッドが非常に複雑で、個別の単体テストが必要な場合、多くの場合、独自のクラスに値することを意味します。これにより、再利用可能な方法で作成することができます。次に、新しいクラスをテストし、古いクラスでそのパブリックインターフェイスを呼び出す必要があります。
一方で、実装の詳細を個別のクラスに分解すると、複雑なインターフェイスを持つクラス、古いクラスと新しいクラスの間でやり取りされる大量のデータ、またはOOPの観点からはよく見えるデザインになりますが、問題ドメインからの直観に一致します(たとえば、プライベートメソッドのテストを回避するために価格設定モデルを2つに分割することはあまり直感的ではなく、コードの保守/拡張時に問題が発生する可能性があります)。常に一緒に変更される「ツインクラス」は必要ありません。
カプセル化とテスタビリティの間の選択に直面したとき、私はむしろ2番目に行きたいです。適切にテストされていなかったため、正しく動作しない素敵なOOPデザインよりも、正しいコードを持つ(つまり、正しい出力を生成する)ことが重要です。Javaでは、メソッドに「デフォルト」アクセス権を与えて、単体テストを同じパッケージに入れることができます。単体テストは、単に開発中のパッケージの一部であり、テストとテスト対象のコードとの間に依存関係があることは問題ありません。つまり、実装を変更するときはテストを変更する必要があるかもしれませんが、それでも構いません-実装を変更するたびにコードを再テストする必要があり、それを行うためにテストを変更する必要がある場合は、それ。
一般に、クラスは複数のインターフェースを提供する場合があります。ユーザー向けのインターフェースと、メンテナー向けのインターフェースがあります。2つ目は、コードを適切にテストするためにさらに公開することができます。プライベートメソッドでの単体テストである必要はありません。たとえば、ロギングなどです。ロギングも「カプセル化を解除」しますが、非常に便利なため、引き続き行います。
プライベートメソッドのテストは、その複雑さに依存します。一部の1行のプライベートメソッドは、テストの余分な労力を実際に保証しません(これはパブリックメソッドとも言えます)。
私が推奨する手法は、プライベートメソッドパッケージをプライベートにすることです。これにより、同じパッケージ内の単体テストにアクセスできますが、それでも他のすべてのコードからカプセル化されます。これにより、(おそらく)複雑なロジックのすべての部分をカバーするためにパブリックメソッドテストに依存する代わりに、プライベートメソッドロジックを直接テストする利点が得られます。
これがGoogle Guavaライブラリの@VisibleForTestingアノテーションとペアになっている場合、このパッケージプライベートメソッドをテストのみに表示されるように明確にマークしているため、他のクラスから呼び出すことはできません。
この手法の反対者は、これによりカプセル化が破られ、プライベートメソッドが開かれて同じパッケージにコーディングされると主張しています。これはカプセル化を破り、プライベートコードを他のクラスに公開することに同意しますが、厳密なカプセル化よりも複雑なロジックのテストの方が重要であり、テストでのみ可視としてマークされているパッケージプライベートメソッドを使用しないことは開発者の責任であると主張しますコードベースの使用と変更。
テスト前のプライベートメソッド:
private int add(int a, int b){
return a + b;
}
テスト可能なパッケージプライベートメソッド:
@VisibleForTesting
int add(int a, int b){
return a + b;
}
注:同じパッケージにテストを配置することは、同じ物理フォルダーにテストを配置することと同等ではありません。メインコードとテストコードを個別の物理フォルダー構造に分離することは一般的に良い習慣ですが、クラスが同じパッケージ内で定義されている限り、この手法は機能します。
外部APIを使用できない場合、または使用したくない場合でも、リフレクションを使用してプライベートメソッドにアクセスするために純粋な標準JDK APIを使用できます。ここに例があります
MyObject obj = new MyObject();
Method privateMethod = MyObject.class.getDeclaredMethod("getFoo", null);
privateMethod.setAccessible(true);
String returnValue = (String) privateMethod.invoke(obj, null);
System.out.println("returnValue = " + returnValue);
Javaチュートリアルhttp://docs.oracle.com/javase/tutorial/reflect/またはJava API http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summaryを確認してください。詳細については、html。
@kijが彼の答えで引用したように、リフレクションを使用した単純なソリューションがプライベートメソッドをテストするのに本当に良い場合があります。
ユニットテストケースとは、コードのユニットをテストすることです。インターフェイスをテストすることを意味するわけではありません。インターフェイスをテストする場合、コード単位をテストすることを意味するわけではありません。それは一種のブラックボックステストになります。また、インターフェイスレベルで問題を特定し、動作していない部分をデバッグしようとするよりも、最小ユニットレベルで問題を見つける方が適切です。したがって、ユニットテストケースは、その範囲に関係なくテストする必要があります。以下は、プライベートメソッドをテストする方法です。
Javaを使用している場合、Deencapsulation.invokeを提供するjmockitを使用して、テスト中のクラスのプライベートメソッドを呼び出すことができます。最終的にそれを呼び出すためにリフレクションを使用しますが、それを囲む素晴らしいラッパーを提供します。(https://code.google.com/p/jmockit/)
まず第一に、他の著者が示唆したように:もし本当にプライベートメソッドをテストする必要があるなら、よく考えてください。そしてそうならば、 ...
.NETでは、これを「Internal」メソッドに変換し、ユニットテストプロジェクトのパッケージ「InternalVisible」を作成できます。
Javaでは、テスト対象のクラスにテスト自体を記述でき、テストメソッドもプライベートメソッドを呼び出すことができます。私は実際には大きなJavaの経験がないので、おそらくベストプラクティスではありません。
ありがとう。
Javaでプライベートメソッドを本当にテストする必要がある場合は、fest assert
and / orを使用できますfest reflect
。リフレクションを使用します。
ライブラリをmavenでインポートします(指定されたバージョンは、私が考える最新のものではありません)。または、クラスパスに直接インポートします。
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-reflect</artifactId>
<version>1.2</version>
</dependency>
例として、パラメータとして文字列を取り、値を「これはクールなテスト!」に更新する「myPrivateMethod」という名前のプライベートメソッドを持つ「MyClass」という名前のクラスがある場合、次のjunitテストを実行できます。
import static org.fest.reflect.core.Reflection.method;
...
MyClass objectToTest;
@Before
public void setUp(){
objectToTest = new MyClass();
}
@Test
public void testPrivateMethod(){
// GIVEN
String myOriginalString = "toto";
// WHEN
method("myPrivateMethod").withParameterTypes(String.class).in(objectToTest).invoke(myOriginalString);
// THEN
Assert.assertEquals("this is cool testing !", myOriginalString);
}
また、このライブラリを使用すると、Beanプロパティ(プライベートでセッターが記述されていなくても)をモックで置き換えることができます。これをMockitoまたは他のモックフレームワークで使用するのは本当にクールです。現時点で知っておく必要があるのは(次のバージョンで改善されるかどうかわからない)、操作するターゲットフィールド/メソッドの名前とその署名だけです。
私が通常C#で行うのは、メソッドをプライベートではなく保護することです。プライベートアクセス修飾子はやや劣りますが、テスト対象のクラスから継承しないすべてのクラスからメソッドを隠します。
public class classUnderTest
{
//this would normally be private
protected void methodToTest()
{
//some code here
}
}
classUnderTestから直接継承しないクラスには、methodToTestが存在することすらわかりません。テストコードでは、このメソッドへのアクセスを拡張および提供する特別なテストクラスを作成できます...
class TestingClass : classUnderTest
{
public void methodToTest()
{
//this returns the method i would like to test
return base.methodToTest();
}
}
このクラスは、私のテストプロジェクトにのみ存在します。その唯一の目的は、この単一のメソッドへのアクセスを提供することです。他のほとんどのクラスにはない場所にアクセスできます。
protected
パブリックAPIの一部であり、同じ制限が発生します(変更することはできず、文書化する必要があります...)。C#では、とinternal
一緒に使用できますInternalsVisibleTo
。
テストするクラスの内部クラスにユニットテストを配置すると、プライベートメソッドを簡単にテストできます。TestNGを使用して、ユニットテストは、次のように@Testアノテーションが付けられたパブリックな静的内部クラスである必要があります。
@Test
public static class UserEditorTest {
public void test_private_method() {
assertEquals(new UserEditor().somePrivateMethod(), "success");
}
}
これは内部クラスであるため、プライベートメソッドを呼び出すことができます。
私のテストはmavenから実行され、これらのテストケースが自動的に検出されます。1つのクラスをテストしたいだけなら
$ mvn test -Dtest=*UserEditorTest
ソース:http : //www.ninthavenue.com.au/how-to-unit-test-private-methods