Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito ClassCastException - A mock cannot be cast

I have a method in the class AppleProcessor which I would like to test:

public void process(Fruit fruit) {
    if(fruit.getType() == Fruit.APPLE) {
        fruitBasket.add(((AppleFruit) fruit).getApple());
    }
    else {
        // do something else
    }
}

Note that Fruit is an interface with the method getType() which AppleFruit implements and also has a getApple() method.

My test looks like:

@Mock
FruitBasket fruitBasket;

@Mock
Fruit fruit;

@Mock
AppleFruit apple;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}

@Test
public void testAnAppleIsProcessed() {
    AppleProcessor appleProcessor = new AppleProcessoer();
    when(fruit.getType()).thenReturn(Fruit.APPLE);
    when(((AppleFruit) fruit).getApple()).thenReturn(apple);

    appleProcessor.process(fruit);

    verify(fruitBasket).add(isA(Apple.class));
}

However I get the following error:

java.lang.ClassCastException: package.fruit.Fruit$$EnhancerByMockitoWithCGLIB$$b8254f54 cannot be cast to package.fruit.AppleFruit

which comes from this line in the test

when(((AppleFruit) fruit).getApple()).thenReturn(apple);

Would anyone know how to resolve this so I can test my code?

like image 660
user2844485 Avatar asked Feb 11 '15 19:02

user2844485


People also ask

How do you overcome 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 a class cast exception?

Introduction. ClassCastException is a runtime exception raised in Java when we try to improperly cast a class from one type to another. It's thrown to indicate that the code has attempted to cast an object to a related class, but of which it is not an instance.

What does initMocks do in Mockito?

initMocks(this); initializes fields annotated with Mockito annotations. Allows shorthand creation of objects required for testing. Minimizes repetitive mock creation code. Makes the test class more readable.

Why can't I type cast on a mock object?

Your mock object is enhanced by Mockito and it is not same as your class, so you can't type cast. Thanks for contributing an answer to Stack Overflow!

Can Mockito mock this class crudrepository?

Because if you start googling around for "Mockito cannot mock this class CrudRepository" you'll hit a lot of articles about how Spring Boot (particularly in regards to the @WebMvcTest annotation) creates the application context and when beans are available and what name they have when they're made available and all that.

How to create an instance of applefruit in Mockito?

Mockito will dynamically create a class which implements Fruit (this class is Fruit$$EnhancerByMockitoWithCGLIB$$b8254f54 ), and create an instance of this class. There's no reason for this class to be an instance of AppleFruit, since you didn't tell Mockito that the object had to be of type AppleFruit.

Why can't I cast an interface to another interface?

because interface is an abstract. You can do an anonymous class implementing this interface: and then you'll be able to cast but you can get exception: ClassCastException if the class is not implementing mentioned interface. because you already have instance created.


2 Answers

When you say

@Mock
Fruit fruit;

You tell Mockito: the fruit variable should be an instance of Fruit. Mockito will dynamically create a class which implements Fruit (this class is Fruit$$EnhancerByMockitoWithCGLIB$$b8254f54), and create an instance of this class. There's no reason for this class to be an instance of AppleFruit, since you didn't tell Mockito that the object had to be of type AppleFruit.

Declare it as AppleFruit, and it will be of type AppleFruit.

like image 187
JB Nizet Avatar answered Sep 19 '22 16:09

JB Nizet


Mockito can deal with mocked objects that are alread casted at mocking time (assignment). However, it does not cast by itself a mocked object during code execution.

In other words(or better yet, code):

Fruit fruit = Mockito.mock(Applefruit.class);

Just do as JB Nizet said and you will be fine. I had a similar problem and his solution worked.

For this question it would be:

@Mock
FruitBasket fruitBasket;

@Mock
AppleFruit fruit; // chaged here

@Mock
AppleFruit apple;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}

@Test
public void testAnAppleIsProcessed() {
    AppleProcessor appleProcessor = new AppleProcessoer();
    when(fruit.getType()).thenReturn(Fruit.APPLE);
    when(((AppleFruit) fruit).getApple()).thenReturn(apple);

    appleProcessor.process(fruit);

    verify(fruitBasket).add(isA(Apple.class));
}

That is all what is needed.

like image 32
Juliano Suman Curti Avatar answered Sep 16 '22 16:09

Juliano Suman Curti