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


104
パブリッククラスA {

    public void method(boolean b){
          if(b == true)
               method1();
          そうしないと
               method2();
    }

    プライベートvoid method1(){}
    プライベートvoid method2(){}
}
パブリッククラスTestA {

    @テスト
    public void testMethod(){
      A a = mock(A.class);
      a.method(true);
      // verify(a).method1();のようにテストする方法
    }
}

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


回答:


81

あなたはMockitoでそれを行うことはできませんが、使用することができますPowermockを Mockitoモックプライベートメソッドを拡張します。PowermockはMockitoをサポートしています。ここに例があります。


19
私はこの答えと混同しています。これはあざけるが、タイトルはプライベートメソッドをテストしている
diyoda_

Powermockを使用してプライベートメソッドをモックしましたが、Powermockでプライベートメソッドをテストするにはどうすればよいですか。どこで、いくつかの入力を渡し、メソッドからの出力を期待して、出力を検証できますか?
Rito

できません。入出力を模擬しています。実際の機能をテストすることはできません。
タルハガム

131

mockitoではできません。彼らのウィキから

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

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

それは決して弾丸の証拠ではないクラスローダーのハッキングを必要とし、それはAPIを変更します(カスタムテストランナーを使用したり、クラスに注釈を付けたりする必要があります)。

回避策は非常に簡単です。メソッドの可視性をプライベートからパッケージ保護(または保護)に変更するだけです。

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

最後に...プライベートメソッドのモックは、オブジェクト指向の理解に何か問題があることのヒントです。オブジェクト指向では、メソッドではなくオブジェクト(またはロール)を協調させたいと考えています。Pascalと手続き型のコードは忘れてください。オブジェクトで考える。


1
このステートメントでは致命的な仮定があります。プライベートメソッドをモックすることは、OOの理解に問題があることのヒントです。パブリックメソッドをテストしていて、それがプライベートメソッドを呼び出している場合、プライベートメソッドの戻り値をモックしたいと思います。上記の仮定に従うと、プライベートメソッドを実装する必要すらなくなります。OOの理解が不十分なのはなぜですか。
eggmatters

1
Baeldungによると、「モック手法は、クラス自体ではなく、クラスの外部依存関係に適用する必要があります。プライベートメソッドのモックがクラスのテストに不可欠である場合、通常、設計が悪いことを示しています。」ここではそれについてのクールなスレッドがあるsoftwareengineering.stackexchange.com/questions/100959/...
ジェイソンGlez

34

これは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);

あなたのリンクは、パワーモックリポジトリ@Mindaugasを指しているだけです
Xavier

@Xavier true。プロジェクトで必要に応じて使用できます。
Mindaugas Jaraminas 2017

1
素晴らしい!!! ほとんどすべてのこれらの単純な例で十分に説明されています:)目的はコードをテストすることであり、すべてのフレームワークが提供するものではないためです:)
siddhusingh

これを更新してください。ホワイトボックスはパブリックAPIの一部ではなくなりました。
user447607

17

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

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

お役に立てれば。


13

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

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

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


ReflectionTestUtils.invokeMethod(student、 "saveOrUpdate"、 "argument1"、 "argument2"、 "argument3"); invokeMethodの最後の引数は、プライベートメソッドに渡す必要がある複数の引数を取ることができるVargsを使用します。できます。
ティム

この答えには、プライベートメソッドをテストする最も簡単な方法である、より多くの投票が必要です。
最大

9

プライベートメソッドをテストすることは想定していません。これらはいずれにせよプライベートメソッドを呼び出す必要があるため、非プライベートメソッドのみをテストする必要があります。プライベートメソッドをテストしたい場合は、設計を再考する必要があることを示している可能性があります。

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

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


26
有効なポイント。ただし、メソッドにプライベート修飾子を使用する理由は、長すぎるコードや繰り返しの多いコードを単純に削除するためです。それを別のクラスとして分離することは、それらのコード行をファーストクラスシチズンに昇格させるようなものです。これは、長いコードを分割し、コード行の繰り返しを防ぐことを目的としていたためです。それを別のクラスに分離しようとする場合、それは正しくないと感じます。あなたは簡単にクラス爆発を起こすでしょう。
supertonsky 2013

2
スーパートンスキーに注意して、私は一般的なケースを参照していました。上記の場合、それは別のクラスにあってはならないことに同意します。(ただし、コメントに+
1-

4
@supertonsky、私はこの問題に対する満足のいく応答を見つけることができませんでした。私がプライベートメンバーを使用する理由はいくつかありますが、多くの場合、それらはコードの臭いを示さず、それらをテストすることから大きな利益を得ます。人々は「ただやらない」と言ってこれを払いのけているようです。
LuddyPants 2014年

3
申し訳ありませんが、「プライベートメソッドをテストしたい場合は、デザインに反対票を投じる必要があるかもしれません」に基づいて反対票を選ぶことにしました。結構ですが、十分に公平ですが、テストを行う理由の1つは、設計を再考する時間がないときに、変更を安全に実装しようとしているためです。プライベートメソッド。理想的な世界では、設計が完璧になるので、プライベートメソッドを変更する必要はありませんか?確かに、完璧な世界ではありますが、テストを必要とする完璧な世界では、すべてが機能するので、それは悲観的です。:)
John Lockwood 2014年

2
@ジョン。ポイントを取ると、あなたの反対票が保証されます(+1)。コメントもありがとう-あなたがした点について私はあなたに同意します。このような場合、2つのオプションのいずれかが表示されます。メソッドをパッケージプライベートまたは保護にして、通常どおりユニットテストを作成します。または(そしてこれは口癖であり、悪い習慣です)メインメソッドは、それがまだ機能することを確認するためにすばやく記述されます。ただし、私の応答は、新しいコードが作成され、元のデザインを改ざんできない場合のリファクタリングではないシナリオに基づいていました。
Jaco Van Niekerk 2014年

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));

6
  1. リフレクションを使用すると、プライベートメソッドをテストクラスから呼び出すことができます。この場合、

    // testメソッドは次のようになります...

    public class TestA {
    
      @Test
        public void testMethod() {
    
        A a= new A();
        Method privateMethod = A.class.getDeclaredMethod("method1", null);
        privateMethod.setAccessible(true);
        // invoke the private method for test
        privateMethod.invoke(A, null);
    
        }
    }
  2. プライベートメソッドが他のプライベートメソッドを呼び出す場合、オブジェクトをスパイして別のメソッドをスタブする必要があります。テストクラスは次のようになります。

    // testメソッドは次のようになります...

    public class TestA {
    
      @Test
        public void testMethod() {
    
        A a= new A();
        A spyA = spy(a);
        Method privateMethod = A.class.getDeclaredMethod("method1", null);
        privateMethod.setAccessible(true);
        doReturn("Test").when(spyA, "method2"); // if private method2 is returning string data
        // invoke the private method for test
        privateMethod.invoke(spyA , null);
    
        }
    }

**アプローチは、オブジェクトの反射とスパイを組み合わせることです。** method1と** method2はプライベートメソッドであり、method1はmethod2を呼び出します。


4

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

いくつかの可能な解決策(AFAIK):

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

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

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

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

  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() {}
    
    }

3

実際には、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.methodからメソッドを呼び出し、SomeOtherClass以下のように書くことができます。

@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(); プライベートメンバーをスパイできる何かでスタブします。


2

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


6
唯一の正当な理由は、レガシーシステムの一部をテストすることです。private / package-privateメソッドのテストを開始すると、オブジェクトの内部が公開されます。これを行うと、通常、リファクタリング可能なコードが貧弱になります。オブジェクト指向システムのすべての優れた点でテスト可能性を実現できるように、構成を参照してください。
Brice、2012年

1
同意-それが好ましい方法です。ただし、mockitoでプライベートメソッドを実際にテストする場合は、これが唯一の(タイプセーフ)オプションです。私の答えは少し急いでいましたが、あなたや他の人がしたように、私はリスクを指摘するべきでした。
Roland Schneider

これは私の好みの方法です。package-privateレベルで内部オブジェクトを公開することは何の問題もありません。ユニットテストはホワイトボックステストです。テストのために内部を知る必要があります。
Andrew Feng

0

プライベートメソッドがvoidではなく、戻り値が外部依存関係のメソッドへのパラメーターとして使用される場合、依存関係をモックし、を使用しArgumentCaptorて戻り値をキャプチャできます。例えば:

ArgumentCaptor<ByteArrayOutputStream> csvOutputCaptor = ArgumentCaptor.forClass(ByteArrayOutputStream.class);
//Do your thing..
verify(this.awsService).uploadFile(csvOutputCaptor.capture());
....
assertEquals(csvOutputCaptor.getValue().toString(), "blabla");
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.