2

このケースでは、JDK6、Junit3、および Mockito 1.8.5 を使用しています (名前はわかりやすいように変更されています)。

public abstract class AbstractProcessorTest<P extends AbstractProcessor<T>, T extends AbstractProcess> {

@Mock(answer = CALLS_REAL_METHODS)
protected P processor;

@Mock(answer = RETURN_DEEP_STUBS)
protected T process;

public void setUp(){
 Mockito.initMocks(this);
 // some common configurations
 processor.setProcess(process);
 ...
}

}

public class ProcessorTest extends AbstractProcessorTest<Processor, ProcessAlpha>{    

@Mock
private Service service;

@Override
public void setUp(){
   super.setUp();
   doReturn(service).when(processor).getService();
}

public void testAMethod(){          
   processor.process();
}

}

testAMethod() テスト ケースを実行すると、次の例外が発生します。

java.lang.ClassCastException: org.xyz.AbstractProcessor$$EnhancerByMockitoWithCGLIB$$c948b334 cannot be cast to org.xyz.ProcessorA

このメソッド呼び出しを調べると、このメソッドがクラス内に見つからないと表示されます。もう 1 つの奇妙な点は、AbstractProcessor.setUp() でメソッドを呼び出しているときに例外が発生しないことです (ただし、ProcessorTest.setUp() では失敗します)。

これは、AbstractProcessorTest クラスを作成する前には発生していなかったので、ジェネリックと Mockito がこれらのオブジェクトをプロキシする方法に関連するものであり、戦略を変更する必要があると思います。

これが十分に明確であることを願っています。前もって感謝します、

セバス~

4

1 に答える 1

4

だから私は以前は実際の問題とは関係がありませんでした。答えはまだ当てはまりますが、ディープスタブの答えを使用するモックがモックを返すと、そのような例外が発生します。

それで、本当の問題は何ですか、それはまだジェネリックと型消去によって引き起こされています。次の 2 つの異なるクラスがあります。

テスト:

public class ProcessorTest extends AbstractProcessorTest<Processor, ProcessAlpha>

そして、テストの親:

public abstract class AbstractProcessorTest<P extends AbstractProcessor<T>, T extends AbstractProcess> {

@Mock(answer = ...)
protected P processor;
  1. そのため、コンパイラは最初に をコンパイルしますAbstractProcessorTest。そうすると、 P が実際には であることがわかり、AbstractProcessorこの方法でクラスがコンパイルされます。
  2. 次に、コンパイラはコンパイルし、それが に解決されることProcessorTestを確認しますが、既にコンパイルされているため変更されません。それでも、可能な限り で考慮されるため、そのバイトコードには可能なキャストオペコードが含まれる場合があります。PProcessorAbstractProcessorTestPProcessorTest
  3. 実行すると、フィールドに基づいてモックをインスタンス化するように請求された現在の Mockito コードは、で修正したタイプではなく、フィールドP processorのタイプを認識します。もちろん、それに応じてモックが作成されます。AbstractProcessorProcessorProcessorTest
  4. メソッドで CCE が発生するのProcessorTest.setUpは、フィールドの一般的な性質により、コンパイラがサイレント キャスト オペコードを確実に導入したためprocessorです。

また、実際のメソッドを呼び出すようにモックを構成するのは非常に奇妙に見えます。モックは状態で初期化されないため、多くの問題が発生する可能性があります。たぶん、代わりにスパイを使いたいですか?

それが役立つことを願っています。


実際には実際の問題に答えていなかった以前の答え。しかし、Mockito のディープ スタブではまだ役に立つかもしれません

はい、現在リリースされているバージョン (1.9.5) のMockitoは、深いスタブを持つジェネリックをサポートしていません (問題 230 を参照) 。したがって、消去後に既知のタイプであるか、他のタイプであるかに関係なく、優れた境界のみが見つかります。Object

ジェネリックは、ランタイムのものよりもコンパイラのものです。Javaの人々が長い間具体化されたジェネリックを好む理由についてGoogleで検索してください。Neal Gafter は 2006 年に http://gafter.blogspot.fr/2006/11/reified-generics-for-java.htmlについて書いていますが、他にも興味深い読み物があります。

編集済み vvvvvvvv 2015-01

ただし、コンパイラは特定のケースでジェネリックに関するデータを埋め込みます。このクラス宣言を使用した例では、public class ProcessorTest extends AbstractProcessorTest<Processor, ProcessAlpha>両方のタイプを読み取ることができます。不格好で遅いリフレクション API を使用します。コードは Mockito コードのマスターに存在し、あなたの例で動作するはずですが、他の問題のためにまだリリースされていません。

Mockito 1.10.x 以降、Mockito はジェネリックをより意識しています。つまり、この宣言のようなモック化された型が型または境界を埋め込んでいる場合はそれらを使用し、メソッドに境界がある場合はそれらを使用します。

つまり、そのようなコードは追加のスタブなしで機能します。つまり、mockito はバイトコードに埋め込まれた境界を検出し、可能であればそれらをモックします (最終的でもプリミティブでもない):

interface UberList<U> extends List<U extends Uber> {
    U firstUber();
    <D extends Driver> D driver();
}

uberList = mock(UberList.class, RETURNS_DEEP_STUB);

Uber u1 = uberList.iterator().next();
Uber u1 = uberList.firstUber();
Driver d = uberList.driver();

編集しました^^^^^^^^

もちろん、ランタイム宣言はまだ発見できません。たとえばList<Processor> pList;Processorジェネリック型の場合、 info はerasedになります。コンパイラがコンパイル中に見つけた唯一の利用可能な情報List。それがどのように行われたかについてあまり詳細を表示しなくても、その型情報はObject、ジェネリック型情報Eの上限がObjectコンパイラによって解決されるように解決されます (これは暗黙の上限であり、書く必要がないのと同じですextends Object)。

したがって、それまでの間、必要なタイプにキャストするか、単に深いスタブの回答を使用しないか、冒険好きな場合は、更新された深いスタブの回答を使用してリリースされていないバージョンの Mockito を自分でコンパイルできます。

それが役立つことを願っています。

于 2013-04-09T16:47:08.243 に答える