web-dev-qa-db-ja.com

mockitoを使用したプライベートメソッドのテスト

 public class A {
 
 public void method(boolean b){
 if(b == true)
 method1(); 
 else 
 method2(); 
} 
 
 private void method1(){} 
 private void method2(){} 
} 
 public class TestA {
 
 @Test 
 public void testMethod(){
 A a = mock(A.class); 
 a.method(true); 
 //verify(a).method1();
} 
} 
のようなテスト方法

プライベートメソッドが呼び出されるかどうかをテストする方法、およびmockitoを使用してプライベートメソッドをテストする方法???

85
Nageswaran

Mockitoでこれを行うことはできませんが、 Powermock を使用してMockitoおよびmockプライベートメソッドを拡張できます。 PowermockはMockitoをサポートしています。 ここ は例です。

71
shift66

Mockitoでは不可能です。 wiki から

Mockitoがプライベートメソッドをモックしないのはなぜですか?

第一に、プライベートメソッドのモックについては独断的ではありません。プライベートメソッドをテストするという観点からはプライベートメソッドが存在しないため、プライベートメソッドについては気にしません。 Mockitoがプライベートメソッドをモックしない理由は次のとおりです。

クラスローダーをハッキングする必要がありますが、これは防弾ではなく、APIを変更します(カスタムテストランナーを使用する、クラスに注釈を付けるなど)。

回避するのは非常に簡単です-メソッドの可視性をprivateからpackage-protected(またはprotected)に変更するだけです。

実装と保守に時間を費やす必要があります。また、ポイント2と別のツール(powermock)で既に実装されているという事実を考えると、意味がありません。

最後に...プライベートメソッドのモックは、OOの理解に問題があることを示すヒントです。 OOでは、メソッドではなく、オブジェクト(またはロール)がコラボレーションすることを望みます。パスカルと手続きコードを忘れてください。オブジェクトで考えます。

118

powermock でそれを行う方法の小さな例を示します

public class Hello {
    private Hello obj;
    private Integer method1(Long id) {
        return id + 10;
    }
} 

テストするためにmethod1コードを使用します:

Hello testObj = new Hello();
Integer result = Whitebox.invokeMethod(testObj, "method1", new Long(10L));

プライベートオブジェクトobjを設定するには、これを使用します。

Hello testObj = new Hello();
Hello newObject = new Hello();
Whitebox.setInternalState(testObj, "obj", newObject);
27

これは、どのような方法があるのか​​ではなく、行動の観点から考えてください。 methodというメソッドは、bがtrueの場合に特定の動作をします。 bがfalseの場合、動作が異なります。これは、methodに対して2つの異なるテストを作成する必要があることを意味します。ケースごとに1つ。したがって、3つのメソッド指向テスト(1つはmethod、1つはmethod1、1つはmethod2)の代わりに、2つの動作指向のテストがあります。

これに関連して(最近、別のSOスレッドでこれを提案し、結果として4文字のWordと呼ばれるようになりました。メソッドの名前ではなく、テストしている動作を反映するテスト名を選択すると便利です。したがって、テストtestMethod()testMethod1()testMethod2()などを呼び出さないでください。私がテストしている動作を示すcalculatedPriceIsBasePricePlusTax()taxIsExcludedWhenExcludeIsTrue()のような名前が好きです。次に、各テストメソッド内で、指定された動作のみをテストします。このような動作のほとんどは、パブリックメソッドへの1回の呼び出しを伴うだけですが、プライベートメソッドへの多くの呼び出しを伴う場合があります。

お役に立てれば。

15

プライベートメソッドをテストする必要はありません。とにかくプライベートメソッドを呼び出す必要があるため、非プライベートメソッドのみをテストする必要があります。プライベートメソッドをテストしたい場合は、デザインを再考する必要があることを示している可能性があります。

適切な依存性注入を使用していますか?プライベートメソッドを別のクラスに移動し、それをテストする必要がありますか?これらのメソッドはプライベートでなければなりませんか? ...それらはむしろデフォルトまたは保護されないのでしょうか?

上記の例では、「ランダムに」呼び出される2つのメソッドを実際に独自のクラスに配置し、テストしてから上記のクラスに注入する必要がある場合があります。

6

リフレクションを使用してmockitoを使用して、内部でプライベートメソッドをテストできました。ここに例があり、それが意味をなすように名前を付けようとしました

//Service containing the mock method is injected with mockObjects

@InjectMocks
private ServiceContainingPrivateMethod serviceContainingPrivateMethod;

//Using reflection to change accessibility of the private method

Class<?>[] params = new Class<?>[]{PrivateMethodParameterOne.class, PrivateMethodParameterTwo.class};
    Method m = serviceContainingPrivateMethod .getClass().getDeclaredMethod("privateMethod", params);
    //making private method accessible
    m.setAccessible(true); 
    assertNotNull(m.invoke(serviceContainingPrivateMethod, privateMethodParameterOne, privateMethodParameterTwo).equals(null));
5

Mockitoはその機能を提供しませんが、Mockito + JUnit ReflectionUtilsクラスまたはSpring ReflectionTestUtils クラスを使用して同じ結果を達成できます。 here からのプライベートメソッドの呼び出し方法を説明する例を参照してください。

ReflectionTestUtils.invokeMethod(student, "saveOrUpdate", "From Unit test");

ReflectionTestUtilsとMockitoの完全な例は、本にあります Mockito for Spring

3
AR1

プライベートメソッドをテストする必要があることを本当に理解していません。根本的な問題は、パブリックメソッドの戻り値の型がvoidであるため、パブリックメソッドをテストできないことです。したがって、プライベートメソッドをテストする必要があります。私の推測は正しいですか??

いくつかの可能なソリューション(AFAIK):

  1. プライベートメソッドをモックしますが、メソッドを「実際に」テストすることはありません。

  2. メソッドで使用されているオブジェクトの状態を確認します。 MOSTLYメソッドは、入力値を処理して出力を返すか、オブジェクトの状態を変更します。目的の状態についてオブジェクトをテストすることもできます。

    public class A{
    
    SomeClass classObj = null;
    
    public void publicMethod(){
       privateMethod();
    }
    
    private void privateMethod(){
         classObj = new SomeClass();
    }
    
    }
    

    [ここでは、nullからnot nullへのclassObjの状態変化を確認することにより、プライベートメソッドをテストできます。]

  3. コードを少しリファクタリングします(これはレガシーコードではありません)。メソッドを書く私の基本は、常に何か(int/boolean)を返す必要があるということです。戻り値は実装で使用される場合と使用されない場合がありますが、テストでは必ず使用されます

    コード。

    public class A
    { 
        public int method(boolean b)
        {
              int nReturn = 0;
              if (b == true)
                   nReturn = method1();
              else
                   nReturn = method2();
        }
    
        private int method1() {}
    
        private int method2() {}
    
    }
    
2
Reji

テストを同じパッケージに入れますが、異なるソースフォルダー(src/main/Java vs. src/test/Java)に入れ、それらのメソッドをパッケージプライベートにします。 Imoのテスト容易性はプライバシーよりも重要です。

1

実際には、Mockitoを使用してプライベートメンバーのメソッドをテストする方法があります。次のようなクラスがあるとしましょう:

public class A {
    private SomeOtherClass someOtherClass;
    A() {
        someOtherClass = new SomeOtherClass();
    }
    public void method(boolean b){
        if (b == true)
            someOtherClass.method1();
        else
            someOtherClass.method2();
    }

}

public class SomeOtherClass {
    public void method1() {}
    public void method2() {}
}

a.methodSomeOtherClassからメソッドを呼び出すことをテストする場合は、次のように記述できます。

@Test
public void testPrivateMemberMethodCalled() {
    A a = new A();
    SomeOtherClass someOtherClass = Mockito.spy(new SomeOtherClass());
    ReflectionTestUtils.setField( a, "someOtherClass", someOtherClass);
    a.method( true );

    Mockito.verify( someOtherClass, Mockito.times( 1 ) ).method1();
}

ReflectionTestUtils.setField();は、プライベートメンバーをスパイできるものでスタブします。

0
Fan Jin