JMockit的功能确实太强了,作为一名程序员,我们还是需要知其然知其所以然以探究技术的本质。看看它背后是如何工作的。


在弄懂JMockit的原理之前,我们先对JMockit的总体有个初步的认识。看看JMockit里面的都有些什么东东。



通过上面的架构图,我们可以看到JMockit有如下核心组件

  1. JVM Attach

      JMockit使用了JDK6动态添加代理功能。目的是为了运行JMockit启动程序做准备。 JMockit提供了不同OS的hotSpot JVM的Attach支持: BsdVirtualMachine, LinuxVirtualMachine,SolarisVirtualMachine,WindowsVirtualMachine。

    JMockit启动程序:主要功能是集成测试框架(JUnit/TestNG),完成对JMockit类转换器织入。

  2. 测试框架集成

    提供了JUnit4/5, TestNG的支持。 


    a)对JUnit4的集成方法:改写JUnit4的核心类org.junit.runner.Runner,org.junit.runners.model.FrameworkMethod, org.junit.runners.model.TestRunnerDecorator,org.junit.runners.model.RunNotifier。改写的目的是为了让测试程序在运行测试方法前,完成Mock 注解API(@Mocked,@Injectable,@Capturing)修饰的测试属性&测试参数的类做相关字节码的织入。
    详见可以见JMockit源代码中Runner类,FakeFrameworkMethod类,JUnit4TestRunnerDecorator类,RunNotifierDecorator类。


    b)对JUnit5/TestNG的集成方法: 由于JUnit5/TestNG支持ServiceLoader的扩展体系,JMockit通过配置/META-INF/services/org.junit.platform.engine.TestEngine,/META-INF/services/org.testng.ITestNGListener完成对JUnit5/TestNG的集成。集成的目的同样是为了让测试程序在运行测试方法前,完成Mock 注解API(@Mocked,@Injectable,@Capturing)修饰的测试属性&测试参数的类做相关字节码的织入。

  3. 字节码处理
    通过ASM,在类的某个方法中加入某段逻辑以达到Mock的目的;生成某个类的子类以支持抽象类的Mock;生成某个接口的实例类以支持接口的Mock。通过ASM, 这些都变得不那么复杂了。

  4. 类转换器
    类转换器是JMockit的核心。Mock的核心就是JMockit不同的类转换器在起作用。

    a)录制(ExpectationsTransformer)

       用于对new Expectations(){{}},new Verifications(){{}},匿名类进行重定义。用于支持测试程序中的录制,重放,校验。

    b)伪类(ClassLoadingBridgeFields)
       伪类,即new MockUp<T> {}的匿名类或 extends MockUp<T>的子类。
       用于伪类的@Mock方法提供支持。 通过识别伪类@Mock方法,在对应的方法体中织入一段分支,用于走伪类的@Mock方法逻辑。

    c)覆盖率(CodeCoverage)
       用于支持JMockit Coverage功能。 通过在类的方法体行加埋点。即可以完成行覆盖率,路径覆盖率的计算。

    d)类缓存(CachedClassfiles)

       这个没有什么好说的,对类进行了重定义,当然要求一个测试方法结束后,能复原类的原有字节码,于是需要一个Cache了。 

    e)对象捕捉(CaptureTransformer)

      用于支持JMockit的withCapture()功能,即捕捉某次测试中,某个类的某个方法的入参是什么,并记录下来。通常用于在验证代码块中,某个方法的入参是否符合期望。

  5. Mock API

@Mocked, @Tested ,@Injectable, @Capturing, MockUp, @Mock ,Expectations, Verifications这些API,通过前面基础知识,常见用法等的学习,这些API已经耳熟能详了吧。 基本能满足大部分的Mock场景了。