cglib与mock,自研mockito

逻辑:

natvie test无法搞定db,也不能在unittest环节搞定——引出mockito

mockito无法处理injectmock 非public方法的when、verify——引出simple mockito(实际上mockto可以处理protected inner function,只要handler和handlertest在同一个package下

 

 

小结论:

1 被mock的对象不用when,什么都不做,有返回的会默认构造一个,我们沿用

 

2 mockito的when、verify参数根据equals方法match,我们沿用

public class MyApplication {
//    @Override
//    public boolean equals(Object obj) {
//        return true;
//    }
}

  

3 unittest为每个@Test建立一个实例,因此MethodInteceptor如下考虑多线程

abstract public class AbstractSimpleMockProxyFactory implements MethodInterceptor {

    /**
     * cglib enhance object -> origin object map
     * 因为2个test会并发跑,多线程读写此static对象,需要concurrent
     */
    public static Map<Object, AbstractSimpleMockProxyFactory> map = new ConcurrentHashMap<>();

    // origin object
    protected Object target;

    /**
     * 同一个mock or injectmock对象只有一条线程操作,因为每个Test方法都创建一个test实例
     * 尽管会并发跑2个test,但读写的是不同的 mocker or inject mocker.counter/whenReturn
     * 不需要concurrent
     */
    protected Map<FunctionMatcher, RetCount> counter = new HashMap<>();
    protected Map<FunctionMatcher, Object> whenReturn = new HashMap<>();

  

4 proxy object->MethodIntecept反向映射

被代理的对象不改写的方法hashcode和equels同样被拦截 java 的三种代理模式 (二)——子函数切面,两种方式:

1)使用System.idendifyHashcode + classname构建key

getClass不在cliib默认拦截范畴内

注意前者极端情况下会冲突 hashcode & System.identityHashCode

2)继续使用proxy object作为hashmap key,但在cglib intecept中放行hashcode equals至基类(除非用户when return了)

       /**
         * 对于@Mock对象,建立cglib enhance object -> origin object map映射时需要
         */
        if(method.getName().equals("hashcode") || method.getName().equals("equals"))
            return proxy.invokeSuper(obj, args);

        /**
         * 对于@Mock对象其它没有whenReturn,也不是hashcode和equals的隐藏未覆盖方法不做处理
         * 如clone、finalize、toString
         * getClass、notify、notifyAll、wait本不在其列
         */
     //   Object returnValue = proxy.invokeSuper(obj, args);

        Class cRet = method.getReturnType();

  

对于@Mock对象,总是为其代理

对于@InjectMock对象,有@Spy时代理,无时使用Test里原始对象

 

10 原理

 

test对象t中,存在@InjectMock,类型A的对象a=new A,依赖C1类型对象b1、C2类型对象b2(ioc或new),t中存在@Mock的C1类型对象d1=null、C2类型对象d2=null,则——

 

1) mockito模式

调用Mockito.initMocks

Mockito为a生成代理da,为d1、d2生成代理pd1、pd2,遍历A的成员,如果类型匹配,则将pd1、pd2赋予,此处da.b1=pd1、da.b2=pd2

 

2)hybrid模式

调用Mockito.initMocks

Mockito为a生成代理da,为d1、d2生成代理pd1、pd2,遍历A的成员,如果类型匹配,则将pd1、pd2赋予,此处da.b1=pd1、da.b2=pd2

调用SimpleMock.injectHybrid

为a生成代理sa,遍历A的成员,sa.b1=da.b1、sa.b2=da.b2

 

3)SimpleMock模式

调用SimpleMock.inject

为a生成代理sa,为d1、d2生成代理sd1、sd2,遍历A的成员,如果类型匹配,则将sd1、sd2赋予,此处sa.b1=sd1、sa.b2=sd2

 

 

不足:

matcher无法从精到粗的匹配,同一个@Test方法不允许对同一个对象,同一种类型(verify or when)同时做any() 和精确值

对@Mock对象的返回为基本类型的方法,无法创建默认对象;对Integer方法 newInstance不能调用有参数构造函数

 

 

2021.2.14 补充mock基类protected方法

其它:

mockito能不能直接 mock @Spy对象 的protected,同包就可以,故使用mockito时,遵循 handler-handlerTest全限定性名相同的规范

但对于非同包的基类protected方法,mockito没有办法,我的SimpleMock由于api而可以

mockito的api设计的太友好了,以至于也有这样的缺点

posted on 2020-10-01 00:20  silyvin  阅读(347)  评论(0编辑  收藏  举报