我有这个案例使用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
当我检查这个方法调用时,它说这个方法在类中找不到。另一个奇怪的事情是我在AbstractProcessor.setUp()中调用方法时没有得到任何异常(但它在ProcessorTest.setUp()中失败)
在我创建AbstractProcessorTest类之前没有发生这种情况,因此我认为这是与泛型有关的一些内容以及Mockito代理这些对象的方式,我需要改变策略。
希望这很清楚。 提前谢谢你,
Sebas .-
答案 0 :(得分:3)
所以我以前与实际问题无关。虽然答案仍然适用,但是当使用深层存根的模拟回答返回模拟时会发生这种异常。
那么真正的问题是什么,它仍然是由泛型和类型擦除引起的。你有两个不同的类:
测试:
public class ProcessorTest extends AbstractProcessorTest<Processor, ProcessAlpha>
测试的父母:
public abstract class AbstractProcessorTest<P extends AbstractProcessor<T>, T extends AbstractProcess> {
@Mock(answer = ...)
protected P processor;
AbstractProcessorTest
,这样做时会看到P实际上是AbstractProcessor
,它会以这种方式编译类。ProcessorTest
,并且会看到P
将被解析为Processor
,但它不会修改AbstractProcessorTest
,因为它已经被编译。在P
中,它仍然会考虑到ProcessorTest
,因此其字节码可能包含可能的强制转换操作码。P processor
中看到AbstractProcessor
类型而不是您在{{1}中修复的类型Processor
}}。当然它会相应地创建模拟。ProcessorTest
方法中引发的,因为由于字段ProcessorTest.setUp
的通用特性,编译器肯定引入了静默转换操作码。同样看起来很奇怪配置模拟来调用实际方法,它可能会导致很多问题,因为模拟没有用状态初始化。也许你想使用间谍?
希望有所帮助。
之前的答案实际上没有回答真正的问题。但是对于Mockito深层存根可能仍然有用
是当前发布的版本(1.9.5)中的Mockito不支持具有深存根(see issue 230)的泛型,因为 Java实现了带有类型擦除的泛型。因此,它只能找到上限,无论是processor
还是擦除后已知的其他类型。
泛型更像是编译器而不是运行时的东西。搜索 google关于为什么java人们想要长期使用reified generics的原因 时间。 Neal Gafter在2006年写了这篇文章 http://gafter.blogspot.fr/2006/11/reified-generics-for-java.html,但是 还有其他有趣的读物。
EDITED vvvvvvvv 2015-01
然而,编译器在某些特定情况下嵌入了一些关于泛型的数据,在这个类声明Object
的示例中,可以读取这两种类型。 使用 clunky 和慢速反射API。该代码存在于Mockito代码的主程序中,应该可以在您的示例中使用,但由于其他问题,它仍然未发布。
由于Mockito 1.10.x Mockito更加了解泛型,即如果像这个声明的模拟类型嵌入类型或边界,它将使用它们,如果方法有边界,它将使用它们。
这意味着像这样的代码可以在没有额外存根的情况下工作,即mockito将发现嵌入在字节码中的边界并在可能的情况下模拟它们(不是最终的或原始的):
public class ProcessorTest extends AbstractProcessorTest<Processor, ProcessAlpha>
EDITED ^^^^^^^^
当然还是无法发现运行时声明。例如,在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
时找到的信息。如果没有显示太多关于如何完成的细节,那么类型信息将被解析为List
,因为泛型类型信息Object
上限将被编译器重新分配到E
(它是隐式的上限,就像你不必写Object
)。
所以,在同一时间你可以投射到想要的类型,只是不要使用深层存根答案,或者如果你有冒险精神,你可以使用更新的深层存根答案编译自己未发行的Mockito版本。< / p>
希望有所帮助。