Skip to content

JMockit 入门

本文示例在 Java 8 中测试通过。

引入依赖

jmockit 的最新版本,必须要指定 -javaagent ,这对一些IDE并不友好。比如在 Intellij IDEA 中无法直接使用右键菜单运行测试类。

如果使用 gradle 构建项目,在 build.gradle 中增加以下配置:

gradle
dependencies {

    testCompile "junit:junit:4.12"
    testCompile "org.jmockit:jmockit:1.46"
  
}

test {
    // jvmArgs "-javaagent:${classpath.find { it.name.contains("jmockit") }.absolutePath}"
    jvmArgs "-javaagent:${configurations.testRuntimeClasspath.find { it.name.contains('jmockit') }}"

    testLogging {
        outputs.upToDateWhen {false}
        showStandardStreams = true
    }

}

对于老版本gradle+jmockit,一般不用指定 jvmArgs 。
若遇到报错 JMockit didn't get initialized; please check the -javaagent JVM initialization parameter was used,说明需要指定下 jvmArgs。

若 jvmArgs 指定后报错 Cannot change dependencies of configuration ':xxx' after it has been included in dependency resolution. 。那么可以换成下面的方式,保证在依赖解析前加上参数:

gradle
test {
    doFirst {
        jvmArgs "-javaagent:${configurations.testRuntimeClasspath.find { it.name.contains('jmockit') }}"
    }
}

使用 Expectations 给返回值打桩

java
package demo;

import mockit.*;
import org.junit.Test;

import java.util.Random;

public class MyJMockitoDemo {

    @Mocked
    Random mockRandom;

    @Test
    public void test01() {
        System.out.println("----test01----");
        System.out.println(mockRandom.nextInt());

        // 指明调用 nextInt 时,返回 123
        new Expectations() {{      // 通过双大括号构造匿名类,并初始化对象
            mockRandom.nextInt(); result = 123;
        }};

        System.out.println(mockRandom.nextInt());
    }

    @Test
    public void test02() {
        System.out.println("----test02----");
        System.out.println(mockRandom.nextInt());
    }

}

在命令行中执行gradle test​,效果如下::

$ gradle test

> Task :test

demo.MyJMockitoDemo > test01 STANDARD_OUT
    ----test01----
    0
    123

demo.MyJMockitoDemo > test02 STANDARD_OUT
    ----test02----
    0

关于双大括号,可以参考 [[1024-Java 匿名类双大括号初始化]]。

使用 MockUp 给返回值打桩

java
package demo;

import mockit.*;
import org.junit.Test;

import java.util.Random;

public class MyJMockitoDemo {

    @Test
    public void test01() {
        System.out.println("----test01----");

        Random random = new Random();
        System.out.println(random.nextInt());   // 输出一个随机数

        new MockUp<Random>() {
            @Mock
            public int nextInt() {
                return 1;
            }
        };

        System.out.println(random.nextInt());   // 输出 1
    }

    @Test
    public void test02() {
        System.out.println("----test02----");
        Random random = new Random();
        System.out.println(random.nextInt());   // 输出一个随机数
    }


}

在命令行中执行gradle test​,效果如下:

$ gradle test

> Task :test

demo.MyJMockitoDemo > test01 STANDARD_OUT
    ----test01----
    -377985168
    1

demo.MyJMockitoDemo > test02 STANDARD_OUT
    ----test02----
    1201440410

MockUp 可以给静态方法打桩

Calculate 类中增加静态方法 add :

java
package demo;

public class Calculate {

    // 注意,这是一个 static 函数
    public static int add(int a, int b) {
        return a+b;
    }

}

测试类代码:

java
package demo;

import mockit.*;
import org.junit.Test;

public class MyJMockitoDemo {

    @Test
    public void test() {

        new MockUp<Calculate>() {
            @Mock
            public int add(int a, int b) {        // 这里不用 static 修饰函数
                return 100;
            }
        };

        System.out.println(Calculate.add(1, 2));  // 输出 100
    }

}

在命令行中执行gradle test​,效果如下:

$ gradle test

> Task :test

demo.MyJMockitoDemo > test01 STANDARD_OUT
    100

原理

基本原理是:通过 javaagent 机制,修改被测试类的字节码。