Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito ClassCastException

The method I want to test has a for loop with logic for each element in bList:

class A {
    void someMethod(){

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

I get an exception when executing following test:

@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();
    }
}

The exception I get is:

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

Has anyone encountered this before and come up with a workaround?

I have searched for a solution and have come across some links: http://code.google.com/p/mockito/issues/detail?id=251 and http://code.google.com/p/mockito/issues/detail?id=107

like image 242
aces. Avatar asked Apr 25 '12 21:04

aces.


People also ask

How do I resolve Java Lang ClassCastException error?

How to handle ClassCastException. To prevent the ClassCastException exception, one should be careful when casting objects to a specific class or interface and ensure that the target type is a child of the source type, and that the actual object is an instance of that type.

What is Mockito any?

Mockito allows us to create mock objects and stub the behavior for our test cases. We usually mock the behavior using when() and thenReturn() on the mock object.


1 Answers

As this link you posted indicates, you've encountered a bug with Answers.RETURNS_DEEP_STUBS.

I don't actually see any reason to actually use RETURNS_DEEP_STUBS in your example code. You really should try to evaluate whether or not you need deep stubs, because, as the Mockito docs say, "every time a mock returns a mock a fairy dies." So if you can, just take that out and your example will work.

However, if you insist on using deep stubs, you can hack around this error by up-casting the return value from the method call to Object. For example, replace the offending line in your code with this:

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

All that being said, I personally agree with @jhericks. The best solution is probably to use an actual ArrayList which contains your mock as opposed to mocking List. The only problem is getting your list injected, so you'd have to use @Spy. For example:

@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
  }
}
like image 183
Tim Pote Avatar answered Oct 27 '22 00:10

Tim Pote