2013-10-31 8 views
6

私はJUnitでテストしたい、MyClassという名前のJavaクラスを持っています。私がテストしたいパブリックメソッドmethodAは、プライベートメソッドmethodBを同じクラスに呼び出し、どの条件付きパスを従うかを決定します。私の目標は、異なるパスのJUnitテストをmethodAに書くことです。また、methodBはサービスを呼び出すため、JUnitテストを実行するときに実際に実行されることは望ましくありません。テスト中の同じクラスのモックプライベートメソッド

methodBをモックしてその戻り値を制御して、 'methodA'のさまざまなパスをテストできる最良の方法は何ですか?

私はモックを書くときにJMockitを使いたいので、私は特にJMockitに適用されるすべての答えに興味があります。ここで

は私の例のクラスである:

public class MyClass { 

    public String methodA(CustomObject object1, CustomObject object2) { 

     if(methodB(object1, object2)) { 
      // Do something. 
      return "Result"; 
     } 

     // Do something different. 
     return "Different Result"; 

    } 

    private boolean methodB(CustomObject custObject1, CustomObject custObject2) { 

     /* For the sake of this example, assume the CustomObject.getSomething() 
     * method makes a service call and therefore is placed in this separate 
     * method so that later an integration test can be written. 
     */ 
     Something thing1 = cobject1.getSomething(); 
     Something thing2 = cobject2.getSomething(); 

     if(thing1 == thing2) { 
      return true; 
     } 
     return false; 
    } 

} 

これは私がこれまで持っているものです。

public class MyClassTest { 
    MyClass myClass = new MyClass(); 

    @Test 
    public void test_MyClass_methodA_enters_if_condition() { 
     CustomObject object1 = new CustomObject("input1"); 
     CustomObject object2 = new CustomObject("input2"); 

     // How do I mock out methodB here to return true? 

     assertEquals(myClass.methodA(object1, object2), "Result"); 
    } 

    @Test 
    public void test_MyClass_methodA_skips_if_condition() { 
     CustomObject object1 = new CustomObject("input1"); 
     CustomObject object2 = new CustomObject("input2"); 

     // How do I mock out methodB here to return false? 

     assertEquals(myClass.methodA(object1, object2), "Different Result"); 
    } 

} 

ありがとう!

+0

を、そのメソッドパッケージプライベートすなわち「デフォルト」のアクセス指定子を作ります。そして私はEasyMock#createMockBuilder()を使って部分模擬を作成し、その私的方法を模擬することができます。あなたのJMockitに相当することは分かりません。 – deepakraut

+0

Easymock-Powermockソリューションに興味がありますか?それ以外の場合は、私はあなたのためにそれを書きたいと思います。 –

+0

も参照してください。http://stackoverflow.com/questions/250692/how-do-you-unit-test-private-methods – Raedwald

答えて

2

あなたは(JMockitの部分モックを使用して)を求めた答えを与える:

public class MyClassTest 
{ 
    @Tested MyClass myClass; 

    @Test 
    public void test_MyClass_methodA_enters_if_condition() { 
     final CustomObject object1 = new CustomObject("input1"); 
     final CustomObject object2 = new CustomObject("input2"); 

     new NonStrictExpectations(myClass) {{ 
      invoke(myClass, "methodB", object1, object2); result = true; 
     }}; 

     assertEquals("Result", myClass.methodA(object1, object2)); 
    } 

    @Test 
    public void test_MyClass_methodA_skips_if_condition() { 
     final CustomObject object1 = new CustomObject("input1"); 
     final CustomObject object2 = new CustomObject("input2"); 

     new NonStrictExpectations(myClass) {{ 
      invoke(myClass, "methodB", object1, object2); result = false; 
     }}; 

     assertEquals("Different Result", myClass.methodA(object1, object2)); 
    } 
} 

しかし、私ははそのようにそれをやってお勧めしません。一般的に、privateのメソッドは嘲笑されるべきではありません。代わりに、テスト(この場合はCustomObject)の下で、あなたのユニットの実際の外部依存関係をあざけり:このシナリオでは

public class MyTestClass 
{ 
    @Tested MyClass myClass; 
    @Mocked CustomObject object1; 
    @Mocked CustomObject object2; 

    @Test 
    public void test_MyClass_methodA_enters_if_condition() { 
     new NonStrictExpectations() {{ 
      Something thing = new Something(); 
      object1.getSomething(); result = thing; 
      object2.getSomething(); result = thing; 
     }}; 

     assertEquals("Result", myClass.methodA(object1, object2)); 
    } 

    @Test 
    public void test_MyClass_methodA_skips_if_condition() { 
     new NonStrictExpectations() {{ 
      object1.getSomething(); result = new Something(); 
      object2.getSomething(); result = new Something(); 
     }}; 

     assertEquals("Different Result", myClass.methodA(object1, object2)); 
    } 
} 
+0

これは私の質問に答えてくれてありがとう!私はここで最初のオプションを使用しましたが、プライベートメソッドを嘲笑することはお勧めできません。私の場合、 'Something'オブジェクトは、いくつかのサービスを呼び出すレガシーコードの連鎖に従わずにインスタンス化することはできません。 'methodB'は' thing1'と 'thing2'の特定のフィールドの値をチェックするので、' methodB'を模倣し、 'Something'や' CustomObject'を模倣しようとするよりも望ましい戻り値を強制する方が簡単でした。 – Kingand

+0

悲しいことに、JMockit 1.23以降、MockupプライベートメソッドのExpectationsサポートが削除されているので、今MockUpを使用する必要があります。 – dag

0

メソッドBを別のクラスのメンバーにして、そのクラスへのプライベートリファレンスをMyClassにします。

public class MyClass { 
    private MyOtherClass otherObject = new MyOtherClass(); 

    public String methodA(CustomObject object1, CustomObject object2) { 

     if(otherObject.methodB(object1, object2)) { 
      // Do something. 
      return "Result"; 
     } 

     // Do something different. 
     return "Different Result"; 

    } 
} 

class MyOtherClass { 
    public boolean methodB(CustomObject custObject1, CustomObject custObject2) { 
     // Yada yada code 
    } 
} 

個人的に、私は通常のみのパブリックメソッドをテストして、すべてのパスが私のプライベートな方法で訪問されていることを確認するために、カバレッジ・レポートを見てください。プライベートメソッドを実際にテストする必要がある場合は、それが上記のリファクタリングを必要とする匂いです。

また、反射を使用することもできますが、私はそれを行うことを汚いと感じるでしょう。あなたが本当にそれが私に知らせる解決策を望むなら、私はこの答えにそれを加えます。プライベートメソッドを模擬するために

+0

私のコード例は、誤解を招く可能性があります。私の目標は、 'methodA'の'何かをする '部分の機能をテストすることです。 'methodA'にif文を入力させて、その中のコードをテストできるようにする方法を探しています。私は 'methodB'をテストしようとすると、それがリファクタリングする必要があるかもしれないということに同意します。 – Kingand

-1

、あなたはpowermock
サンプルコードは次のようになりますが、私はそれを実行していないが必要です。

import org.mockito.Mockito; 
    import org.powermock.api.mockito.PowerMockito; 
    import org.powermock.modules.junit4.PowerMockRunner; 

    @RunWith (PowerMockRunner.class) 
    public class MyClassTest { 

     @Test 
     public void test_MyClass_methodA_enters_if_condition() { 
      final MyClass myClass = Mockito.mock (MyClass.class); 
      CustomObject object1 = new CustomObject("input1"); 
      CustomObject object2 = new CustomObject("input2"); 
      Mockito.when (myClass.methodB(object1, object2)).thenReturn (true); 
      Mockito.when (myClass.methodA(object1, object2)).thenCallRealMethod(); 

      assertEquals(myClass.methodA(object1, object2), "Result"); 
     } 
    } 
+1

私はこれがコンパイルされるとは思わない。コンパイラは 'methodB()'がプライベートであると不満を持ちます。 PowerMockには、メソッド名の文字列引数をとるプライベートメソッドを呼び出す手段があります。ちょっと醜いですね。 – mikeslattery

+0

あなたは正しいです、私はプライベートメソッドではないサンプルを提供します。しかし、このリンクは役に立ちます。 http://stackoverflow.com/questions/7803944/how-to-mock-private-method-for-testing-using-powermock – Lifecube

3

あなたはモックツールを使用して、そうする策略に従事することができたとしても、プライベートメソッドを模擬するように誘惑されないでください。プライベートメンバーは実装の詳細なので、自由に変更する必要があります。代わりに非プライベートAPIを使用してクラスを実行します。これが面倒であれば、面倒なコードを別のクラスに移動することを検討してください(まだ存在しない場合)。依存関係注入を使用して面倒なコードを実装してください。

1
import org.easymock.EasyMock; 
import org.junit.Assert; 
import org.junit.Before; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.powermock.api.easymock.PowerMock; 
import org.powermock.core.classloader.annotations.PrepareForTest; 
import org.powermock.modules.junit4.PowerMockRunner; 

@RunWith(PowerMockRunner.class) 
@PrepareForTest({ MyClass.class }) 
public class MyClassTest { 

// Class Under Test 
MyClass cut; 

@Before 
public void setUp() { 

    // Create a new instance of the service under test (SUT). 
    cut = new MyClass(); 

    // Common Setup 
    // TODO 
} 

@Test 
public void testMethodA() throws Exception { 

    /* Initialization */ 
    CustomObject object2 = PowerMock.createNiceMock(CustomObject.class); 
    CustomObject object1 = PowerMock.createNiceMock(CustomObject.class); 

    MyClass partialMockCUT = PowerMock.createPartialMock(MyClass.class, 
      "methodB"); 
    long response = 1; 

    /* Mock Setup */ 
    PowerMock 
      .expectPrivate(partialMockCUT, "methodB", 
        EasyMock.isA(CustomObject.class), 
        EasyMock.isA(CustomObject.class)).andReturn(true) 
      .anyTimes(); 

    /* Mock Setup */ 

    /* Activate the Mocks */ 
    PowerMock.replayAll(); 

    /* Test Method */ 

    String result = partialMockCUT.methodA(object1, object2); 

    /* Asserts */ 
    Assert.assertNotNull(result); 
    PowerMock.verifyAll(); 

} 

} 
関連する問題