2017-02-15 10 views
3

私はJavaで抽象クラスをテストする必要があり、結果を提供するためにequals()に依存するメソッドがあります。equals()に依存する抽象クラスのメソッドをMockitoでユニットテストする方法は?

public abstract class BaseClass<T extends BaseClass<T>> { 

    public boolean isCanonical() { 
     return toCanonical() == this; 
    } 

    public T toCanonical() { 
     T asCanonical = asCanonical(); 
     if (equals(asCanonical)) { 
      //noinspection unchecked 
      return (T) this; 
     } 
     return asCanonical; 
    } 

    protected abstract T asCanonical(); 
} 

あなたはtoCanonical()メソッドが抽象メソッドasCanonical()呼び出すasCanonicalビルドで自分自身を比較する見ることができるように。

これをテストするには、抽象クラスの空の実装を作成し、実装された2つのメソッドの実際のメソッドを呼び出して、asCanonical()の独自のクラスを返します。これはequals()方法ではなかった場合

BaseClass aCanonicalClass; 
BaseClass aNonCanonicalClass; 

@Mock 
BaseClass sut; 

@Before 
public void setUp() { 
    MockitoAnnotations.initMocks(this); 
    // these are the methods under test, use the real ones 
    Mockito.when(sut.isCanonical()) 
      .thenCallRealMethod(); 
    Mockito.when(sut.toCanonical()) 
      .thenCallRealMethod(); 
} 

通常、私はちょうどこれを行うだろう:このエラーを与える

@Test 
public void test_isCanonical_bad() { 
    // test true 
    Mockito.when(sut.equals(aCanonicalClass)) 
      .thenReturn(true); 
    Mockito.when(sut.equals(aNonCanonicalClass)) 
      .thenReturn(false); 
    Mockito.when(sut.asCanonical()) 
      .thenReturn(aCanonicalClass); 
    Assert.assertTrue(sut.isCanonical()); 

    // test false 
    Mockito.when(sut.equals(aNonCanonicalClass)) 
      .thenReturn(true); 
    Mockito.when(sut.equals(aCanonicalClass)) 
      .thenReturn(false); 
    Mockito.when(sut.asCanonical()) 
      .thenReturn(aCanonicalClass); 
    Assert.assertFalse(sut.isCanonical()); 
} 

を:

Mockitoの制限の
org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'. 
For example: 
    when(mock.getArticles()).thenReturn(articles); 

Also, this error might show up because: 
1. you stub either of: final/private/equals()/hashCode() methods. 
    Those methods *cannot* be stubbed/verified. 
    Mocking methods declared on non-public parent classes is not supported. 
2. inside when() you don't call method on mock but on some other object. 

一つは、それはdoesnのことですequals()hashcode()メソッドをモックすることができます。

回避策として、条件equals() = falseをテストするときに条件equals() = trueと別のランダムオブジェクトをテストするときには、自分自身を渡すだけです。

@Test 
public void test_isCanonical() { 
    // test true 
    Mockito.when(sut.asCanonical()) 
      .thenReturn(sut); 
    Assert.assertTrue(sut.isCanonical()); 

    // test false 
    Mockito.when(sut.asCanonical()) 
      .thenReturn(aCanonicalClass); 
    Assert.assertFalse(sut.isCanonical()); 
} 

しかし、このテストでは、私がこれまでの実装を変更した場合でも渡す:

public T toCanonical() { 
     T asCanonical = asCanonical(); 
     if (this == asCanonical) { 
      //noinspection unchecked 
      return (T) this; 
     } 
     return asCanonical; 
    } 

かさえ、この:

public T toCanonical() { 
     return asCanonical(); 
    } 

間違っています!比較はequalsで行う必要があります。参照することと同じことではありません。

物事は私がtoCanonical()方法を取得するときにテストすることは不可能で取得します。もちろん、これは動作しません

@Test 
public void test_toCanonical_bad() { 
    // test canonical 
    Mockito.when(sut.equals(aCanonicalClass)) 
      .thenReturn(true); 
    Mockito.when(sut.equals(aNonCanonicalClass)) 
      .thenReturn(false); 
    Mockito.when(sut.asCanonical()) 
      .thenReturn(aCanonicalClass); 
    Assert.assertSame(sut, sut.toCanonical()); 

    // test non-canonical 
    Mockito.when(sut.equals(aNonCanonicalClass)) 
      .thenReturn(true); 
    Mockito.when(sut.equals(aCanonicalClass)) 
      .thenReturn(false); 
    Mockito.when(sut.asCanonical()) 
      .thenReturn(aCanonicalClass); 
    Assert.assertNotEquals(sut, sut.toCanonical()); 
    Assert.assertSame(aCanonicalClass, sut.toCanonical()); 
} 

、と私はちょうどそれをテストしますので、実際には同じ回避策を適用することは理にかなっていません関数の戻り値は、asCanonical()の一つである:事実を私はequals()の結果を模擬することはできませんので

@Test 
public void test_toCanonical() { 
    // test canonical 
    Mockito.when(sut.asCanonical()) 
      .thenReturn(sut); 
    Assert.assertSame(sut, sut.toCanonical()); 

    // test non-canonical 
    Mockito.when(sut.asCanonical()) 
      .thenReturn(aCanonicalClass); 
    Assert.assertNotEquals(sut, sut.toCanonical()); 
    Assert.assertSame(aCanonicalClass, sut.toCanonical()); 
} 

両試験は無意味です。

私はそれを与えたオブジェクトで少なくともequals()が呼び出されていますか? (スパイ)

これをテストする他の方法はありますか?

EDIT: this is a semplification of the actual code I'm working with.

I modified the example code to at least show the toCanonical() and asCanonical() methods are supposed to return instances of the same class (and not just any class)

I want to test the base class here. Only the concrete implementation know how to build an actual canonical class (asCanonical()) or check if this class is equal to a canonical class (equals()). And please note that toCanonical() return ITSELF if it is equals to the canonical version of itself. Again equals != same .

The actual implementations are immutable classes. And since they are many and this code is the same for everyone i put it in a base class.

+0

私はなぜあなたが 'BaseClass'の空でない実装を作成してそこにすべての必要なテストロジックを置くことができないのか分かりませんか?現在のところ、何らかの理由でMockitoを使わずにセットアップテストをすることを禁じているようです。 – user3707125

+0

これは私のACTUALコードの簡略化されたコードであり、質問をすることができる範囲でそれを絞り込んだものです。私は 'BaseClass'の実装を一つも持っていません。私の抽象クラスをテストするには、何をするのだろうか?私は空の実装をテストしなければなりません。サブクラスが 'toCanonical()'をオーバーライドしたい場合は自由であり、独自のテストを行います。 –

答えて

1

なぜ抽象クラスをテストしますか? toCanonicalメソッド(実際にはfinalではないので、後でオーバーライドすることができます)をテストする場合は、一時的な具象クラスをインスタンス化してテストすることができます。そう簡単にしてMockitoと他のものを使用しています。

すなわち:

@Test 
public void myAwesomeTest() { 
    // Arrange 
    BaseClass test = new BaseClass<String>() { 
     // provide concrete implementation 
     protected String asCanonical() { 
      return "Foo"; 
     } 
    }; 

    // Act 
    String result = test.toCanonical("Bar"); 

    // Assert 

} 

単純なケースが動作したら、あなたはMakeBaseClass(String valueToReturnForAsCanonicalすなわち、ファクトリメソッドにbaseclassの作成を抽出し、より多くのテストを書くことができます。

スパイを使用すると、テストされていないコードの一部の地域にいる場合や、コード内で部分を切り抜いたり、コード内に縫い目を作成したりすることができない場合に便利です。この場合、Mockitoの魔法に頼るのではなく、自分自身で定型文を書くだけで十分だと思います。

幸運を祈る!

+0

これは 'toCanonical()'関数をどのようにテストするのか分かりません。 関数の仕事は 'asCanonical()'を呼び出すことであり、返された値が 'equals()'であればそれを返します。そうでなければ生成された値を返します。具体的なクラスのみが標準バージョンを処理する方法を知っているか、2つのクラスが同一であるかどうかを知っています。 私は実際のコードに近づくように私の質問を編集しました(これはもちろん、semplificationです)。 –

+0

私の編集をチェックして、他の部分、 'toCanonical()'関数のテストを追加し、semplifiedの例を修正しました –

+0

'toCanonical()'を呼び出し、順番に 'asCanonical'を呼び出します。どのようにしても、具体的なクラスをインスタンス化してテストすることができます。または、匿名クラスを使用します。 –

関連する問題