青年IT男

个人从事金融行业,就职过易极付、思建科技等重庆一流技术团队,目前就职于某网约车平台负责整个支付系统建设。自身对金融行业有强烈的爱好。同时也实践大数据、数据存储、自动化集成和部署、分布式微服务、人工智能等领域。

JMockit原理剖析 -各Mock注解的Mock逻辑

JMockit原理剖析 -各Mock注解的Mock逻辑

JMockit提供了很多Mock注解,例如@Mocked, @Injectable,@Tested,@Mock, @Capturing, 那这些注解背后的Mock逻辑是什么呢?

   在搞清楚这个问题之前,我们得首先明白,Mock本质是对java字节码的修改(或重定义)。 那我们先看看java字节码里面有什么。拿一个简单的类来说,
//一个包含初始代码块的普通类 
public class AnOrdinaryClassWithBlock {
    private int i;

    public static int j;

    // 初始代码块
    {
        i = 1;
    }
    // 静态初始代码块
    static {
        j = 2;
    }

    // 构造函数
    public AnOrdinaryClassWithBlock(int i) {
        this.i = i;
    }

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }

}

   它编译成.class文件后,字节码内容如下:
public class cn/jmockit/demos/AnOrdinaryClassWithBlock {
     <ClassVersion=52>
     <SourceFile=AnOrdinaryClassWithBlock.java>

     private int i;
     public static int j;

     static  { // <clinit> //()V
         L1 {
             iconst_2
             putstatic cn/jmockit/demos/AnOrdinaryClassWithBlock.j:int
         }
         L2 {
             return
         }
     }

     public AnOrdinaryClassWithBlock(int arg0) { // <init> //(I)V
         <localVar:index=0 , name=this , desc=Lcn/jmockit/demos/AnOrdinaryClassWithBlock;, sig=null, start=L1, end=L2>
         <localVar:index=1 , name=i , desc=I, sig=null, start=L1, end=L2>

         L1 {
             aload0 // reference to self
             invokespecial java/lang/Object.<init>()V
         }
         L3 {
             aload0 // reference to self
             iconst_1
             putfield cn/jmockit/demos/AnOrdinaryClassWithBlock.i:int
         }
         L4 {
             aload0 // reference to self
             iload1
             putfield cn/jmockit/demos/AnOrdinaryClassWithBlock.i:int
         }
         L5 {
             return
         }
         L2 {
         }
     }

     public getI() { //()I
         <localVar:index=0 , name=this , desc=Lcn/jmockit/demos/AnOrdinaryClassWithBlock;, sig=null, start=L1, end=L2>

         L1 {
             aload0 // reference to self
             getfield cn/jmockit/demos/AnOrdinaryClassWithBlock.i:int
             ireturn
         }
         L2 {
         }
     }

     public setI(int arg0) { //(I)V
         <localVar:index=0 , name=this , desc=Lcn/jmockit/demos/AnOrdinaryClassWithBlock;, sig=null, start=L1, end=L2>
         <localVar:index=1 , name=i , desc=I, sig=null, start=L1, end=L2>

         L1 {
             aload0 // reference to self
             iload1
             putfield cn/jmockit/demos/AnOrdinaryClassWithBlock.i:int
         }
         L3 {
             return
         }
         L2 {
         }
     }
}
     我们可以看到, 静态代码块以及静态变量的初始化代码,是放进了clinit方法里, 代码块以及成员变量初始化是放到init(即构造函数)方法里,此外类还有其它方法,未初始化的类的静态变量,成员变量。 


   JMockit对java字节码的修改,最终就是对方法的修改,注入JMockit自己的代码。下面,我们总结一下,各个注解的Mock逻辑。
Mock注解 Mock范围 Mock逻辑 用途
@Mocked 类/接口 默认除了clinit方法外,其它所有方法都被Mock。如果注解的stubOutClassInitialization=true, 则clinit也会被mock. Mock后方法返回原始类型的就返回默认值,比如int就返回0.方法返回String的就返回null,方法返回其它引用的,就返回另一个@Mocked的对象(递归的定义) 把类的所有对象,所有方法都Mock
@Injectable 对象 除clinit方法外,其它方法都会被Mock。init虽没有被Mock,但init并不会调用。即@Injectable修饰的对象的构建不会调用init方法。Mock后与@Mocked相同 对某一个对象进行Mock
@Tested 不Mock 与@Injectable搭配使用@Tested表示待测试的对象,该对象由JMockit自动帮你创建。
@Mock 方法 @Mock修饰的方法,代替原方法。
@Capturing 类/接口及其所有子类 是@Mocked的加强版,还作用于其所有子类 用于Mock子类
0
1028826685@qq.com