之前已经介绍过Mockito和PowerMock的常见用法,PowerMock其实就是在Mockito的基础上使用了字节码技术使得其可以对静态方法,私有方法等进行插桩。
现在就先来看看Mockito是怎么创建一个mock对象的(本文的源码都是来自mockito-core-3.2.0,低版本的底层实现不是ByteBuddy)
先看下一个比较简单的Mockito例子
@Component
public class UserDao {
public List<User> queryUserByName(String name) {
User user = new User("1", "chenpp", 18);
List<User> list = new ArrayList<User>();
list.add(user);
return list;
}
}
public class User {
private String id;
private String userName;
private Integer age;
public User(String id, String userName, Integer age) {
this.id = id;
this.userName = userName;
this.age = age;
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public List<User> queryUser(String userName){
System.out.println("查询条件userName:"+ userName);
return userDao.queryUserByName(userName);
}
}
@RunWith(JUnit4.class)
public class UserServiceTest {
@InjectMocks
private UserService userService;
@Mock
private UserDao userDao;
@Before
public void init(){
MockitoAnnotations.initMocks(this);
}
@Test
public void test(){
List<User> list = new ArrayList<User>();
User user1 = new User("2", "chenpp", 18);
User user2 = new User("3", "chenpp", 18);
list.add(user1);
list.add(user2);
Mockito.when(userDao.queryUserByName(eq("chenpp"))).thenReturn(list);
List userList1 = userService.queryUser("chenpp");
Assert.assertTrue(userList1.size() == 2);
List userList2 = userService.queryUser("chenpp2");
Assert.assertTrue(userList2.size() == 0);
}
}
对于一个最基本的mock单测,一般包含如下几个部分:
1.构建被mock的对象 userDao
2.构建使用mock对象的实际对象 userService
3.建立mock对象和实际对象的关系
4.对mock对象进行插桩
明显可以看出这里的入口方法是 MockitoAnnotations.initMocks(this); ,那么看看这个方法是怎么帮我们分别构建mock对象和实际对象并将其建立联系的
MockitoAnnotations.initMocks(this)
public static void initMocks(Object testClass) {
if (testClass == null) {
throw new MockitoException("testClass cannot be null. For info how to use @Mock annotations see examples in javadoc for MockitoAnnotations class");
} else {
//先获取注解引擎插件 -- 这里拿到的是InjectingAnnotationEngine(里面有个delegate : IndependentAnnotationEngine)
AnnotationEngine annotationEngine = (new GlobalConfiguration()).tryGetPluginAnnotationEngine();
//执行引擎的process方法
annotationEngine.process(testClass.getClass(), testClass);
}
}
InjectingAnnotationEngine
分别执行IndependentAnnotationEngine和SpyAnnotationEngine的process方法, 这两个引擎分别对mock对象和spy对象进行实例化,然后对添加了InjectMocks注解的对象进行注入和实例化
public void process(Class<?> clazz, Object testInstance) {
this.processIndependentAnnotations(testInstance.getClass(), testInstance);
this.processInjectMocks(testInstance.getClass(), testInstance);
}
private void processIndependentAnnotations(Class<?> clazz, Object testInstance) {
//clazz是当前的测试类,从当前测试类开始向上寻找所有需要mock或者spy的对象
for(Class classContext = clazz; classContext != Object.class; classContext = classContext.getSuperclass()) {
//delegate : IndependentAnnotationEngine
this.delegate.process(classContext, testInstance);
this.spyAnnotationEngine.process(classContext, testInstance);
}
}
private void processInjectMocks(Class<?> clazz, Object testInstance) {
for(Class classContext = clazz; classContext != Object.class; classContext = classContext.getSuperclass()) {
this.injectMocks(testInstance);
}
}
看下IndependentAnnotationEngine的process方法:
通过反射拿到当前clazz的所有field, 判断其上是否有@Mock注解,如果有则通过this.createMockFor(annotation,field)方法创建mock的代理对象
public void process(Class<?> clazz, Object testInstance) {
Field[] fields = clazz.getDeclaredFields();
Field[] var4 = fields;
int var5 = fields.length;
//遍历测试类的所有field
for(int var6 = 0; var6 < var5; ++var6) {
Field field = var4[var6];
boolean alreadyAssigned = false;
Annotation[] var9 = field.getAnnotations();
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
Annotation annotation = var9[var11];
//判断是否有添加@Captor或者@Mock注解,如果有则创建代理对象
Object mock = this.createMockFor(annotation, field);
if (mock != null) {
this.throwIfAlreadyAssigned(field, alreadyAssigned);
alreadyAssigned = true;
try {
//通过反射将创建的mock对象赋值给测试对象的对应属性
FieldSetter.setField(testInstance, field, mock);
} catch (Exception var15) {
throw new MockitoException("Problems setting field " + field.getName() + " annotated with " + annotation, var15);
}
}
}
}
}
最终通过如下代码创建Mock对象, 可以看到 Mockito.mock(type, mockSettings);方法,就是早期没有注解的时候用于创建mock对象的api
public static Object processAnnotationForMock(Mock annotation, Class<?> type, String name) {
//创建MockSettings
MockSettings mockSettings = Mockito.withSettings();
if (annotation.extraInterfaces().length > 0) {
mockSettings.extraInterfaces(annotation.extraInterfaces());
}
if ("".equals(annotation.name())) {
mockSettings.name(name);
} else {
mockSettings.name(annotation.name());
}
if (annotation.serializable()) {
mockSettings.serializable();
}
if (annotation.stubOnly()) {
mockSettings.stubOnly();
}
if (annotation.lenient()) {
mockSettings.lenient();
}
mockSettings.defaultAnswer(annotation.answer());
//创建mock对象的核心方法
return Mockito.mock(type, mockSettings);
}
创建Mock对象
核心方法如下:
MockitoCore
/**
* @param typeToMock mock的接口或类
* @param settings mock对象的配置
* */
public <T> T mock(Class<T> typeToMock, MockSettings settings) {
//判断settings是否是MockSettingsImpl的实例,如果不是则抛出异常
if (!MockSettingsImpl.class.isInstance(settings)) {
throw new IllegalArgumentException("Unexpected implementation of '" + settings.getClass().getCanonicalName() + "'\nAt the moment, you cannot provide your own implementations of that class.");
} else {
MockSettingsImpl impl = (MockSettingsImpl)MockSettingsImpl.class.cast(settings);
//前面强转后校验Mock class和配置信息(底层是调用了MockSettingsImpl的validatedSettingsa方法)
MockCreationSettings<T> creationSettings = impl.build(typeToMock);
//[1]真正生成mock对象的方法
T mock = MockUtil.createMock(creationSettings);
//开始执行mock,调用各种MockCreation监听器的onMockCreated回调方法
ThreadSafeMockingProgress.mockingProgress().mockingStarted(mock, creationSettings);
return mock;
}
}
[1] 真正创建mock对象的核心方法,主要做三件事情:
- 创建MockHandler
- 创建mock对象
- 判断settings里是否有spy对象来确定返回spy对象还是mock对象(主要是针对@Spry注解)
public static <T> T createMock(MockCreationSettings<T> settings) {
//通过MockHandlerFactory创建mock的处理器
MockHandler mockHandler = MockHandlerFactory.createMockHandler(settings);
//根据处理器以及settings创建mock对象
T mock = mockMaker.createMock(settings, mockHandler);
//获取spy对象,如果对象有spy实例,则替换掉前面生成的mock对象
Object spiedInstance = settings.getSpiedInstance();
if (spiedInstance != null) {
new LenientCopyTool().copyToMock(spiedInstance, mock);
}
return mock;
}
MockHandler
这里使用的是装饰器模式,先创建一个MockHandlerImpl的handler对象,然后先后用NullResultGuardian,InvocationNotifierHandler包装起来
public static <T> MockHandler<T> createMockHandler(MockCreationSettings<T> settings) {
//核心处理器
MockHandler<T> handler = new MockHandlerImpl<T>(settings);
//处理null和基本类型的返回结果
MockHandler<T> nullResultGuardian = new NullResultGuardian<T>(handler);
//通知监听者,进行各种方法回调
return new InvocationNotifierHandler<T>(nullResultGuardian, settings);
}
其作用是给每个MockCreationSettings创建其mockHandler,然后在执行mock/spy对象的方法时,都会先被handler拦截,如果有进行插桩,则执行返回插桩的结果,如果没有,则返回默认值或者真实执行结果
MockHandlerImpl
先简单分析到这里,后面再来看这部分源码
public Object handle(Invocation invocation) throws Throwable {
...
// 根据invoation查找对应stubbing
StubbedInvocationMatcher stubbing = invocationContainer.findAnswerFor(invocation);
if (stubbing != null) {
//有插桩则返回插桩结果
stubbing.captureArgumentsFrom(invocation);
try {
return stubbing.answer(invocation);
} finally {
mockingProgress().reportOngoingStubbing(ongoingStubbing);
}
} else {
//没有则使用DefaultAnswer应答并返回结果值
Object ret = mockSettings.getDefaultAnswer().answer(invocation);
invocationContainer.resetInvocationForPotentialStubbing(invocationMatcher);
return ret;
}
}
createMock
先来看下mockMarker这个常量
private static final MockMaker mockMaker = Plugins.getMockMaker();
//Plugins.java
public static MockMaker getMockMaker() {
return registry.getMockMaker();
}
//PluginRegistry.java
private final PluginSwitch pluginSwitch = new PluginLoader(new DefaultPluginSwitch())
.loadPlugin(PluginSwitch.class);
private final MockMaker mockMaker = new PluginLoader(pluginSwitch, DefaultMockitoPlugins.INLINE_ALIAS)
.loadPlugin(MockMaker.class);
MockMaker getMockMaker() {
return mockMaker;
}
这里的PluginSwitch只有一个方法isEnabled(String pluginClassName),表示当前插件是否可用
还有一个比较重要的急速PluginLoader对象的loadPlugin方法,看名字应该是根据class加载插件的,我们看下其实现
<PreferredType, AlternateType> Object loadPlugin(final Class<PreferredType> preferredPluginType, final Class<AlternateType> alternatePluginType) {
try {
//先根据Class类型通过类加载器加载对应插件
PreferredType preferredPlugin = initializer.loadImpl(preferredPluginType);
if (preferredPlugin != null) {
return preferredPlugin;
} else if (alternatePluginType != null) {
//如果没有则尝试通过其他的PluginType加载插件
AlternateType alternatePlugin = initializer.loadImpl(alternatePluginType);
if (alternatePlugin != null) {
return alternatePlugin;
}
}
//都没有的话就会使用默认的插件
return plugins.getDefaultPlugin(preferredPluginType);
} catch (final Throwable t) {
//如果异常则通过jdk动态代理创建该类型插件的代理对象,不过其在执行的时候会抛出异常
return Proxy.newProxyInstance(preferredPluginType.getClassLoader(),
new Class<?>[]{preferredPluginType},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
throw new IllegalStateException("Could not initialize plugin: " + preferredPluginType + " (alternate: " + alternatePluginType + ")", t);
}
});
}
}
PluginInitializer.java
使用类加载器去 **mockito-extensions/{className}**路径下查找相应的资源, 由于在Mockitio jar包的classpath下不存在mockito-extensions目录,所以都是使用默认的插件类。即:PluginSwitch使用的插件类是DefaultPluginSwitch(其isEnabled方法始终返回true,即认为所有插件都是可用的),MockMaker使用的是ByteBuddyMockMaker(使用ByteBuddy创建mock对象)
ByteBuddy是java字节码增强框架,可以动态的生成java字节码文件,比起我们自己进行字节码文件的生成,它屏蔽了底层细节,提供一套统一易上手的Api
public <T> T loadImpl(Class<T> service) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
loader = ClassLoader.getSystemClassLoader();
}
...
resources = loader.getResources("mockito-extensions/" + service.getName());
try {
//findPluginClass里就根据pluginSwitch的isEnabled方法对resources进行了过滤
String classOrAlias = new PluginFinder(pluginSwitch).findPluginClass(Iterables.toIterable(resources));
//找到对应的PluginClass之后通过反射实例化
if (classOrAlias != null) {
//如果找到的class名称和指定的别名一样,就通过别名去plugins里预先加载的插件信息里获取class名称(个人感觉这里设计的太复杂了,为了获得一个class名称)
if (classOrAlias.equals(alias)) {
classOrAlias = plugins.getDefaultPluginClass(alias);
}
Class<?> pluginClass = loader.loadClass(classOrAlias);
//通过反射实例化
Object plugin = pluginClass.newInstance();
//强转
return service.cast(plugin);
}
return null;
} catch (Exception e) {
throw new IllegalStateException(
"Failed to load " + service + " implementation declared in " + resources, e);
}
}
再回到createMock这个方法,通过前面的分析知道这里的mockMaker实际是ByteBuddyMockMaker的实例,看下其实现
T mock = mockMaker.createMock(settings, mockHandler);
ByteBuddyMockMaker.java
private ClassCreatingMockMaker defaultByteBuddyMockMaker = new SubclassByteBuddyMockMaker();
@Override
public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) {
//这里是SubclassByteBuddyMockMaker的实例
return defaultByteBuddyMockMaker.createMock(settings, handler);
}
SubclassByteBuddyMockMaker.java
可以看到,最终调用的是SubclassByteBuddyMockMaker的createMock方法,先根据settings创建mock的代理类(这里生成的就是com.ibu.itinerary.UserDao$MockitoMock$2020018071 )
@Override
public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) {
//[1]根据settings创建mock的代理类
Class<? extends T> mockedProxyType = createMockType(settings);
//[2]获取Instantiator用于实例化
Instantiator instantiator = Plugins.getInstantiatorProvider().getInstantiator(settings);
T mockInstance = null;
try {
//实例化代理对象,支持构造器和Objenesis创建实例(没有特别设置的话使用的就是默认的ObjenesisInstantiator)
mockInstance = instantiator.newInstance(mockedProxyType);
MockAccess mockAccess = (MockAccess) mockInstance;
//[3]使用MockMethodInterceptor设置代理类的拦截器
mockAccess.setMockitoInterceptor(new MockMethodInterceptor(handler, settings));
//确认生成的mock对象与我们预期的mockType是匹配的
return ensureMockIsAssignableToMockedType(settings, mockInstance);
} catch (ClassCastException cce) {
...
}
}
[1] createMockType
public SubclassByteBuddyMockMaker(SubclassLoader loader) {
//注意这里传入的BytecodeGenerator的实例类型是SubclassBytecodeGenerator,因为使用的是只带一个参数的构造方法,matcher参数是any(),后面会使用到
cachingMockBytecodeGenerator = new TypeCachingBytecodeGenerator(new SubclassBytecodeGenerator(loader), false);
}
@Override
public <T> Class<? extends T> createMockType(MockCreationSettings<T> settings) {
try {
//根据settings构建MockFeatures,再通过TypeCachingBytecodeGenerator创建mock对象的代理类
return cachingMockBytecodeGenerator.mockClass(MockFeatures.withMockFeatures(
settings.getTypeToMock(),
settings.getExtraInterfaces(),
settings.getSerializableMode(),
settings.isStripAnnotations()
));
} catch (Exception bytecodeGenerationFailed) {
throw prettifyFailure(settings, bytecodeGenerationFailed);
}
}
TypeCachingBytecodeGenerator.java
public <T> Class<T> mockClass(final MockFeatures<T> params) {
try {
ClassLoader classLoader = params.mockedType.getClassLoader();
//从一个typeCache的类型缓存里根据classLoader和MockitoMockKey获取mock的代理类,如果没有就创建一个
return (Class<T>) typeCache.findOrInsert(classLoader,
new MockitoMockKey(params.mockedType, params.interfaces, params.serializableMode, params.stripAnnotations),
//这是一个匿名内部类,bytecodeGenerator实际是SubclassBytecodeGenerator的实例
new Callable<Class<?>>() {
@Override
public Class<?> call() throws Exception {
return bytecodeGenerator.mockClass(params);
}
}, BOOTSTRAP_LOCK);
}
...
}
//TypeCache的findOrInsert方法 一开始肯定是没有缓存的,所以调用insert方法
public Class<?> findOrInsert(ClassLoader classLoader, T key, Callable<Class<?>> lazy) {
Class<?> type = this.find(classLoader, key);
if (type != null) {
return type;
} else {
//在执行insert之前会先调用Callable的call方法,也就是SubclassBytecodeGenerator的mockClass方法
return this.insert(classLoader, key, (Class)lazy.call());
}
}
SubclassBytecodeGenerator主要就是使用ByteBuddy的api生成代理类,可以看到其生成的类名规则如下:
String name = String.format("%s$%s$%d", typeName, "MockitoMock", Math.abs(random.nextInt()));
[2]getInstantiator获取构造器
和前面分析的一样,这里的Plugins.getInstantiatorProvider(),最终获取到的是默认的InstantiatorProvider2插件,也就是DefaultInstantiatorProvider的实例对象,也就是说
Instantiator instantiator = Plugins.getInstantiatorProvider().getInstantiator(settings);
DefaultInstantiatorProvider.java
private final static Instantiator INSTANCE = new ObjenesisInstantiator();
public Instantiator getInstantiator(MockCreationSettings<?> settings) {
//如果settings里有指定构造器,就使用指定的
if (settings != null && settings.getConstructorArgs() != null) {
return new ConstructorInstantiator(settings.getOuterClassInstance() != null, settings.getConstructorArgs());
} else {
//没有就使用默认的ObjenesisInstantiator构造器(底层就是使用了ObjenesisStd)
return INSTANCE;
}
}
【3】MockMethodInterceptor 拦截mock对象的所有方法
前面说过拦截mock对象后执行的是MockHandlerImpl的handle方法,那么是怎么调用到这个拦截方法的呢?
先来看一下 bytebuddy 拦截过程,如下:
就是通过method和intercept 分别制定拦截的方法和拦截后返回的结果
Class<?> dynamicType = new ByteBuddy()
// 指定父类
.subclass(Object.class)
// 根据名称来匹配需要拦截的方法
.method(ElementMatchers.named("toString"))
// 拦截方法调用,返回固定值
.intercept(FixedValue.value("Hello World!"))
// 产生字节码
.make()
// 加载类
.load(getClass().getClassLoader())
// 获得Class对象
.getLoaded();
Assert.assertEquals("Hello World!", dynamicType.newInstance().toString());
那再去看下Mockito是怎么通过ByteBuddy构建的代理类
private final Implementation dispatcher = to(DispatcherDefaultingToRealMethod.class);
private final Implementation hashCode = to(MockMethodInterceptor.ForHashCode.class);
private final Implementation equals = to(MockMethodInterceptor.ForEquals.class);
DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType)
.name(name)
.ignoreAlso(isGroovyMethod())
.annotateType(features.stripAnnotations
? new Annotation[0]
: features.mockedType.getAnnotations())
.implement(new ArrayList<Type>(features.interfaces))
//这里的matcher是any(),表示会拦截任一方法,拦截后会调用DispatcherDefaultingToRealMethod类里的方法
.method(matcher)
.intercept(dispatcher)
.transform(withModifiers(SynchronizationState.PLAIN))
.attribute(features.stripAnnotations
? MethodAttributeAppender.NoOp.INSTANCE
: INCLUDING_RECEIVER)
//设置isHashCode和isEquals方法拦截后执行的方法,分别是MockMethodInterceptor的ForHashCode和ForEquals方法
.method(isHashCode())
.intercept(hashCode)
.method(isEquals())
.intercept(equals)
.serialVersionUid(42L)
//设置一个MockMethodInterceptor类型的字段,并让其实现MockAccess接口,这也是前面可以将mock对象转成MockAccess对象的原因
.defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE)
.implement(MockAccess.class)
.intercept(FieldAccessor.ofBeanProperty());
通过源码可以看出来,这里拦截mock对象的方法调用后,会将其交给mockitoInterceptor处理,调用其doIntercept方法,最终调用了其handler的handle方法,也就是一开始说的MockHandlerImpl的handle方法
public static class DispatcherDefaultingToRealMethod {
//我们通常调用mock方法拦截后进入的应该是interceptSuperCallable方法
@SuppressWarnings("unused")
@RuntimeType
@BindingPriority(BindingPriority.DEFAULT * 2)
public static Object interceptSuperCallable(@This Object mock,
@FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor,
@Origin Method invokedMethod,
@AllArguments Object[] arguments,
@SuperCall(serializableProxy = true) Callable<?> superCall) throws Throwable {
if (interceptor == null) {
return superCall.call();
}
return interceptor.doIntercept(
mock,
invokedMethod,
arguments,
new RealMethod.FromCallable(superCall)
);
}
@SuppressWarnings("unused")
@RuntimeType
public static Object interceptAbstract(@This Object mock,
@FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor,
@StubValue Object stubValue,
@Origin Method invokedMethod,
@AllArguments Object[] arguments) throws Throwable {
if (interceptor == null) {
return stubValue;
}
return interceptor.doIntercept(
mock,
invokedMethod,
arguments,
RealMethod.IsIllegal.INSTANCE
);
}
}