2014-06-19 3 views
0

일반 매개 변수가있는 추상 테스트 클래스가 있습니다.ClassCastException Mockito를 사용하여 제네릭 형식을 조롱 할 때

public abstract class AbstractCarTest<G> { 
    ... 
    @Mock 
    protected G carMock; 
    ... 
} 

나는 구체적인 테스트 클래스를 구현했습니다. TruckTest를 실행할 때

public class TruckTest extends AbstractCarTest<Truck> { 
    ... 
    when(truckFactory.getTruck(anyString()).return(carMock); 
    ... 
} 

방법 서명 내가 왜 즉 ClassCastException

java.lang.ClassCastException: org.mockito.internal.creation.jmock.ClassImposterizer$ClassWithSuperclassToWorkAroundCglibBug$$EnhancerByMockitoWithCGLIB$$... cannot be cast to com.example.Truck 

받기

public Truck getTruck(String name); 

처럼 보인다? 그걸 해결할 수있는 방법은 없나요?

+0

PowerMock을 사용하고 있습니까? – geoand

+0

아니요, 그냥 평범한 모키토 – Will

답변

1

구체적인 유형을 모르는 경우 모치 토가 수업을 조롱하지 않는 것처럼 보입니다.

다음을 수행하여 문제를 해결 얻을 수 있습니다 :

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

OP 질문에 'static'이라는 단어가 포함되어 있지 않습니다 ... – Ray

+0

죄송합니다.내 예제를 좀 더 간단하게 끓여 내려고 노력했지만 분명히 조금 단순화했다. truckFactory는 일부 팩토리 클래스의 인스턴스라고 가정한다. – Will

+0

수입품과 의존성을 보여줄 수 있습니까? – geoand

2

Mockito 런타임에 당신의 모의 클래스를 생성한다. Mock은 주어진 클래스를 서브 클래스 화하고 모든 메소드를 오버라이드 (override)함으로써 생성됩니다. 따라서 final 클래스 나 메소드를 조롱하지 말아야한다는 제한이 있습니다. 런타임시 모두 generic types are erased이며 상위 유형 바인드로 대체됩니다. AbstractCarTest에 명시 적 상한을 지정하지 않으므로 Object입니다. 따라서, Mockito는 원시 클래스를 보는 등 : 런타임에

public abstract class AbstractCarTest { 

    @Mock 
    protected Object carMock; 
} 

하고 원하는 Truck 클래스 대신 Object를 확장하는 모의를 생성합니다. (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()); 
} 

을 제네릭을 사용하지 않고 조롱 유형을 정의함으로써, 당신은 게터에 의해 추상 기본 클래스에서 모의에 액세스 할 수있는 조롱 유형이 올바르게 정의된다 Truck.

0

@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()에 제공된 색인은 유형 인수가 선언에 표시되는 위치에 따라 달라집니다.

public abstract class AbstractCarTest<A, B, C, G> 

그리고 당신은 당신이 공급하고 3의 인덱스해야 할 것 ClassG의를 사용하고 싶었 :이 같은이있는 경우 대부분의 경우이 그러나 0 될 것입니다.