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 | 类/接口 |
| 把类的所有对象,所有方法都Mock |
@Injectable | 对象 |
| 对某一个对象进行Mock |
@Tested | 不Mock | 与@Injectable搭配使用。 @Tested表示待测试的对象,该对象由JMockit自动帮你创建。 | |
@Mock | 方法 | @Mock修饰的方法,代替原方法。 | |
@Capturing | 类/接口及其所有子类 | 是@Mocked的加强版,还作用于其所有子类 | 用于Mock子类 |