我有一个带有通用参数的抽象测试类。
public abstract class AbstractCarTest<G> {
...
@Mock
protected G carMock;
...
}
我已经实施了一个具体的测试类。
public class TruckTest extends AbstractCarTest<Truck> {
...
when(truckFactory.getTruck(anyString()).return(carMock);
...
}
方法签名如下所示
public Truck getTruck(String name);
运行TruckTest
时,我得到ClassCastException
说
java.lang.ClassCastException: org.mockito.internal.creation.jmock.ClassImposterizer$ClassWithSuperclassToWorkAroundCglibBug$$EnhancerByMockitoWithCGLIB$$... cannot be cast to com.example.Truck
为什么?我可以用任何方式解决这个问题吗?
答案 0 :(得分:2)
Mockito在运行时生成你的模拟类。通过子类化给定类并覆盖其所有方法来创建模拟。 (因此没有模拟final
类或方法的限制。)在运行时,所有generic types are erased并由其上限类型替换,在AbstractCarTest
这是Object
,因为你不指定显式上限。因此,Mockito将您的原始类视为:
public abstract class AbstractCarTest {
@Mock
protected Object carMock;
}
在运行时,将创建一个扩展Object
而不是您想要的Truck
类的模拟。 (您无法看到这一点,因为cglib有一个无法直接扩展Object
的错误。相反,Mockito正在扩展一个名为ClassWithSuperclassToWorkAroundCglibBug
的内部类。)通常,编译器会在编译时发出类型错误泛型类型仍然可用,但在运行时,您会遇到heap pollution。
解决方法如下:
public abstract class AbstractCarTest<T> {
protected abstract T getCarMock();
// define some base test cases here that use the generic type.
}
public class TruckTest extends AbstractCarTest<Truck> {
@Mock
private Truck carMock;
@Override
protected Truck getCarMock() { return carMock; }
// ...
when(truckFactory.getTruck(anyString()).return(getCarMock());
}
通过在不使用泛型的情况下定义模拟类型,您可以通过getter访问抽象基类中的模拟,其中模拟类型被正确定义为Truck
。
答案 1 :(得分:1)
当Mockito不知道它的具体类型时,它似乎不会嘲笑它。
您可以通过执行以下操作来解决问题:
public abstract class AbstractCarTest<G> {
...
protected G carMock;
...
@Before
public void setUp() throws Exception {
carMock= Mockito.mock(getClazz());
}
protected abstract Class<G> getClazz();
}
public class TruckTest extends AbstractCarTest<Truck> {
...
when(truckFactory.getTruck(anyString()).return(carMock);
...
@Override
protected Class<Truck> getClazz() {
return Truck.class;
}
}
答案 2 :(得分:1)
只想改进@geoand的答案。
public abstract class AbstractCarTest<G> {
...
protected G carMock;
...
@Before
public void setUp() throws Exception {
carMock= Mockito.mock(getClazz());
}
private Class<G> getClazz() {
return (Class<G>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
}
public class TruckTest extends AbstractCarTest<Truck> {
...
when(truckFactory.getTruck(anyString()).return(carMock);
...
}
这最终消除了对抽象方法的需求。提供给getActualTypeArguments()
的索引将取决于Type Argument在声明中的显示位置。在大多数情况下,这将是0
,但如果你有这样的事情:
public abstract class AbstractCarTest<A, B, C, G>
并且您希望使用Class
G
来提供3
所需的A = C + A[k-1:n]
。