Mockito ClassCastException

Метод, который я хочу протестировать, имеет цикл for с логикой для каждого элемента в bList:

class A {
    void someMethod(){

        for(B b: bList){
            //some logic for b
        }
    }
}

Я получаю исключение при выполнении следующего теста:

@RunWith(MockitoJUnitRunner.class)
class ATest {

    @Mock
    private B b;

    @Mock
    private Map<Int, List<B>> bMap;

    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    private List<B> bList;

    @Spy
    @InjectMocks
    private C c;
    ....

    @Test
    public void test(){

        //this line executes fine
        when(bList.size()).thenReturn(1);

        //strangely this works fine
        when(bMap.get(any())).thenReturn(bList);

        //ClassCastException
        when(bList.get(0)).thenReturn(b); // or when(bList.get(anyInt())).thenReturn(b);

        c.methodIWantToTest();
    }
}

Исключением я получаю:

java.lang.ClassCastException:
org.mockito.internal.creation.jmock.ClassImposterizer$ClassWithSuperclassToWorkAroundCglibBug$$EnhancerByMockitoWithCGLIB$$ cannot be cast to xyz.B

Кто-нибудь сталкивался с этим раньше и придумал обход?

Я искал решение и наткнулся на некоторые ссылки: http://code.google.com/p/mockito/issues/detail?id=251  а также http://code.google.com/p/mockito/issues/detail?id=107

Ответ 1

Как эта ссылка, которую вы опубликовали, вы обнаружили ошибку с Answers.RETURNS_DEEP_STUBS.

Я действительно не вижу причин использовать RETURNS_DEEP_STUBS в вашем примере кода. Вы действительно должны попытаться оценить, нужны ли вам глубокие заглушки, потому что, как Mockito docs говорят, "каждый раз, когда макет возвращает макет фея умирает". Поэтому, если вы можете, просто возьмите это, и ваш пример будет работать.

Однако, если вы настаиваете на использовании глубоких заглушек, вы можете взломать эту ошибку, добавив возвращаемое значение из вызова метода в Object. Например, замените строку нарушения в коде следующим образом:

when((Object)bList.get(0)).thenReturn(b);

Все, что сказано, я лично соглашаюсь с @jhericks. Лучшим решением, вероятно, является использование фактического ArrayList, который содержит ваш макет, а не насмешку List. Единственная проблема заключается в получении вашего списка, поэтому вам придется использовать @Spy. Например:

@RunWith(MockitoJUnitRunner.class)
class ATest{
  private B b = mock(B.class);
  @Spy
  private List<B> bList = new ArrayList<B>() {{ add(b); }};

  @InjectMocks
  private C c = new C();

  @Test
  public void test(){
    c.methodIWantToTest();
    // verify results
  }
}

Ответ 2

На самом деле я бы искал проблемы с classpath и relaoding. В списках рассылки mockito и трекера о проблемах некоторые сообщенные проблемы можно было отследить до неправильного пути к классам (неправильная версия jar и т.д.) И перезагрузки класса (некоторые банки были перезагружены, но не mockito, а затем привели к созданию экземпляра класса с неправильным загрузчиком классов).

@Aces Не могли бы вы предоставить более подробную информацию, такую ​​как версия и имя используемого вами инструмента (maven, specs, surefire, Play Framework, JRebel, возможно, и т.д.)

Ответ 3

К сожалению, это невозможно

Случай: тесты API:

interface ConfigurationBuilder {...}
configurationBuilder.newServerAction("s1").withName("111")....create();

Основной причиной этого использования является поддержание совместимости во время компиляции. Но mockito не может поддерживать дженерики в цепочках с параметрами RETURNS_MOCKS и RETURNS_DEEP_STUBS из-за стирания типа в java:

Builder/*<ServerActionBuilder>-erasured generic*/ b = configurationBuilder.newServerAction("s1");
b.withName("111")...create();

Результат в приведенном выше примере должен быть ServerAction, но в mockito это объект сгенерированного класса.

см. Проблема: не удается вернуть глубокие заглушки из универсального метода, который возвращает общий тип # 484