0

I have a abstract test class with a generic parameter.

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

I've implemented a concrete test class of it.

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

The method signature looks like this

public Truck getTruck(String name);

When running TruckTest I get a ClassCastException saying

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

Why is that? Any way I can work around that?

2
  • Are you using PowerMock? Commented Jun 19, 2014 at 8:54
  • No, just plain Mockito Commented Jun 19, 2014 at 11:43

3 Answers 3

3

Mockito is generating your mock class at runtime. Mocks are created by subclassing a given class and by overriding all of its methods. (Thus the limitation of not mocking final classes or methods.) At runtime, all generic types are erased and replaced by their upper type bound, in your AbstractCarTest this is Object as you do not specify an explicit upper bound. Therefore, Mockito sees your raw class as:

public abstract class AbstractCarTest {

  @Mock
  protected Object carMock;
}

at runtime and will create a mock that extends Object instead of your desired Truck class. (You cannot see this because cglib has a bug where it cannot extend Object directly. Instead, Mockito is extending an internal class called ClassWithSuperclassToWorkAroundCglibBug.) Usually, the compiler would issue a type error at compile time where the generic type is still available but at runtime, you experience heap pollution instead.

A work around would be as follows:

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

By defining your mocked type without using generics, you are able to access the mock from the abstract base class by a getter where the mocked type is correctly defined as Truck.

Sign up to request clarification or add additional context in comments.

Comments

1

It seems like Mockito will not mock a class when it does not know it's concrete type.

You can get around the problem by doing the following:

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;
    }
}

5 Comments

Sorry. I tried to boil down my example to something simpler and obviously oversimplified a bit.. Assume truckFactory is an instance of some factory class.
Note that you gain some advantages by using @Mock instead of mocking by using the static mock method such as an implicit naming of the mock instance after its field name.
@raphw That is correct, but it this case @Mock doesn't seem to be able to perform it's fundamental task, which is to create a mock :)
@geoand It generates a mock, but of the erased type. Mockito only sees the field at runtime.
@raphw Yes of course it only seems the field at runtime, when the Generic type has been lost. The fact remains, that @Mock will not perform the correct mocking needed in a test scenario like the one mentioned by OP
1

Just wanted to improve a bit on the answer from @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);
    ...
}

This ends up removing the need for the abstract method. The index supplied to getActualTypeArguments() will depend on where the Type Argument appears in your declaration. In most cases this will be 0 however if you have something like this:

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

And you wanted to use the Class of G you would need to supply and index of 3.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.