Javaプロジェクトに取り組んでいます。ユニットテストは初めてです。Javaクラスのプライベートメソッドをユニットテストする最良の方法は何ですか?
通常、プライベートメソッドを直接単体テストすることはありません。それらはプライベートなので、実装の詳細を考慮してください。それらの1つを呼び出して、それが特定の方法で機能することを期待する人は誰もいません。
代わりに、パブリックインターフェイスをテストする必要があります。プライベートメソッドを呼び出すメソッドが期待どおりに機能している場合は、拡張によってプライベートメソッドが正しく機能していると見なします。
一般的に、私はそれを避けます。プライベートメソッドが非常に複雑で、個別の単体テストが必要な場合は、多くの場合、独自のクラスに値することを意味します。これは、再利用可能な方法でそれを書くことを奨励するかもしれません。次に、新しいクラスをテストし、古いクラスでそのクラスのパブリックインターフェイスを呼び出す必要があります。
一方、実装の詳細を個別のクラスに分解すると、複雑なインターフェースを持つクラス、古いクラスと新しいクラスの間で渡される大量のデータ、またはOOPポイントから見栄えのよいデザインにつながる場合がありますビューの問題ですが、問題ドメインからの直感に一致しません(たとえば、プライベートメソッドのテストを回避するために価格モデルを2つに分割することは非常に直感的ではなく、後でコードを保守/拡張するときに問題が発生する可能性があります)。常に一緒に変更される「ツインクラス」を使用する必要はありません。
カプセル化とテスト容易性のどちらかを選択する場合は、2番目の方法を選びます。適切にテストされていないため、正しく機能しないNice OOP設計よりも、正しいコードを使用する(つまり、正しい出力を生成する)ことが重要です。 Javaでは、メソッドに「デフォルト」のアクセス権を与えて、単体テストを同じパッケージに入れるだけです。ユニットテストは単に開発中のパッケージの一部であり、テストとテストされるコードの間に依存関係があっても問題ありません。つまり、実装を変更する場合、テストを変更する必要があるかもしれませんが、それは問題ありません。実装を変更するたびにコードを再テストする必要があり、テストを変更する必要がある場合は、それを行うだけです。それ。
一般に、クラスは複数のインターフェースを提供している場合があります。ユーザー用のインターフェースとメンテナー用のインターフェースがあります。 2番目のものは、コードを適切にテストするために、より多くを公開できます。これは、プライベートメソッドの単体テストである必要はありません。たとえば、ロギングなどです。ロギングも「カプセル化を解除」しますが、それは非常に便利なため、まだ実行しています。
プライベートメソッドのテストは、その複雑さに依存します。一部の1行のプライベートメソッドは、テストの余計な労力を実際には保証しません(これはパブリックメソッドとも言えます)が、一部のプライベートメソッドはパブリックメソッドと同じくらい複雑で、パブリックインターフェイスを介してテストすることが難しい場合があります。
私が好む手法は、プライベートメソッドパッケージをプライベートにすることです。これにより、同じパッケージ内のユニットテストにアクセスできますが、他のすべてのコードからカプセル化されます。これにより、(おそらく)複雑なロジックのすべての部分をカバーするためにパブリックメソッドテストに依存する必要がなく、プライベートメソッドロジックを直接テストできるという利点があります。
これがGoogle Guavaライブラリの@VisibleForTestingアノテーションとペアになっている場合は、このパッケージのプライベートメソッドをテスト用にのみ表示可能として明確にマークしているため、他のクラスから呼び出されるべきではありません。
この手法の反対者は、これによりカプセル化が解除され、プライベートメソッドを開いて同じパッケージ内のコードを作成すると主張しています。これによりカプセル化が解除され、プライベートコードが他のクラスに開かれることに同意しますが、複雑なロジックのテストはstrictカプセル化よりも重要であり、明確にマークされているテストでのみ表示可能であることは、コードベースを使用および変更する開発者の責任である必要があります。
テスト前のプライベートメソッド:
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では、これを「内部」メソッドに変換し、パッケージ「 InternalVisible 」を単体テストプロジェクトに作成できます。
Javaでは、テスト対象のクラスにテスト自体を記述でき、テストメソッドもプライベートメソッドを呼び出すことができるはずです。私は実際には大きなJava経験なので、それはおそらくベストプラクティスではありません。
ありがとう。
私は通常、そのようなメソッドを保護します。クラスが次の場所にあるとします。
src/main/Java/you/Class.Java
次のようにテストクラスを作成できます。
src/test/Java/you/TestClass.Java
これで、保護されたメソッドにアクセスでき、それらをユニットテストできます(JUnitまたはTestNGは実際には重要ではありません)が、これらのメソッドを不要な呼び出し元から保持します。
これはmavenスタイルのソースツリーを想定していることに注意してください。
本当にプライベートメソッドをテストする必要がある場合は、Javaということです。つまり、fest assert
および/または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」という名前のクラスがあり、パラメータとして文字列を受け取る場合、その値を「this is cool Testing!」に更新すると、次の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();
}
}
このクラスは私のテストプロジェクトにのみ存在します。その唯一の目的は、この単一のメソッドへのアクセスを提供することです。他のほとんどのクラスにはない場所にアクセスできます。
テストするクラスの内部クラスに単体テストを配置すると、プライベートメソッドを簡単にテストできます。 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