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
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.
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.
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With