Mockito无法mock/spy情况下的解决方法

Mockito不支持mock/spy如下的类型:

  • final classes
  • anonymous classes
  • primitive types

如果你mock这些类型,会抛出如下错误信息:
org.mockito.exceptions.base.MockitoException: Cannot mock/spy class java.lang.String Mockito cannot mock/spy following: - final classes - anonymous classes - primitive types

而如果你有确实有此需求,可以通过如下方法解决。先定义一个工具类,里面包含如下方法:

public static void injectField(final Object injectable, final String fieldname, final Object value) {
  try {
    final java.lang.reflect.Field field = injectable.getClass().getDeclaredField(fieldname);
    final boolean origionalValue = field.isAccessible();
    field.setAccessible(true);
    field.set(injectable, value);
    field.setAccessible(origionalValue);
  } catch (final NoSuchFieldException | IllegalAccessException e) {
    throw new RuntimeException(e.getMessage(), e);
  }
}

比如你有如下类需要测试:

public class Greeting {
  @Inject
  private String greeting;

  @Override
  public String toString() {
    return this.greeting;
  }
}

那么,你的测试类大概如下:

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

public class GreetingTest {
  @Test
  public void testToString() throws Exception {
    Greeting greeting = new Greeting();
    injectField(greeting, "greeting", "Hello, world");
    assertThat(greeting.toString(), is("Hello, world"));
  }
}

以上参考:https://dzone.com/articles/field-injection-when-mocking-frameworks-fail

当然,如果你使用Spring Test,你可以使用org.springframework.test.util.ReflectionTestUtils。 类似ReflectionTestUtils.setField(testObject, "person", mockedPerson);

或者,如果你使用Mockito 1.*,可以使用
import org.mockito.internal.util.reflection.FieldSetter;
new FieldSetter(obj, obj.getClass().getDeclaredField("p")).set(new P());

//or
org.mockito.internal.util.reflection.Whitebox#setInternalState(Object target, String field, Object value)

针对Mockito 2.*,可以使用:
org.mockito.internal.util.reflection.FieldSetter.setField(Object target, Field field, Object value)