JMockit:使用 MockUp 的 tearDown 恢复函数行为


#Java Jmockit#


介绍

MockUp 示例:

MockUp<Calculator> mock = new MockUp<Calculator>() {
    @Mock
    public int add(int a, int b) {
        return -1;
    }
};

对象被 MockUp 后,相当于原始类函数字节码中插入了 MockUp 指定的逻辑,如果要恢复原始类函数的行为,可以用 MockUp 的 tearDown 方法。也就是:

mock.tearDown();

但不是所有的 JMockit 版本都支持 tearDown。根据 https://jmockit.github.io/changes.html, tearDown 方法,在 1.1 版本中被加入,1.26 版本中标注废弃,1.28 版本中被删除。

作者认为 tearDown 应该自动被调用,而不是手动被调用;在一个单测函数中多次调用 tearDown 是不合理的,应该拆分成多个单测函数。

1.2 版本中的相关变更:JMockit automatically tears down all mock classes applied on a given context, when said context (the test method/class/ suite or a test preceded by a "before" method) is exited. If explicit tear down really is needed, then the MockUp class and its tearDown() method will have to be used instead. 1.26 版本中的相关变更:Mock-up tear down is automatic, so explicit calls to this method are either made by mistake, or by misuse in tests that should be broken down into two or more test methods.

作者所说的自动 tear down ,在最新的一些测试框架中不一定支持。

例如,下面这个 gradle 配置,不支持自动 tear down:

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testCompile "org.jmockit:jmockit:1.46"
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}

test {
    useJUnitPlatform()

    jvmArgs "-javaagent:${classpath.find { it.name.contains("jmockit") }.absolutePath}"

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

下面的 gradle 配置,支持自动 tear down:

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testCompile "org.jmockit:jmockit:1.46"
    testImplementation 'junit:junit:4.13.2'
}

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

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

如何验证是否支持自动 tearDown ?

package demo;

public class Calculator {

    // 非静态函数
    public int add(int a, int b) {
        return a+b;
    }

    // 静态函数
    public static int staticAdd(int a, int b) {
        return a+b;
    }

    // 空函数
    public void noop() {
    }

}
package demo;

import mockit.Mock;
import mockit.MockUp;
import org.junit.jupiter.api.Test;


public class CalculatorTest {

    @Test
    public void test_add_01() {
        System.out.println("--test_add_01--");
        Calculator calculator = new Calculator();
        System.out.println("mock 之前");
        System.out.println(calculator.add(1, 2));

        // mock
        new MockUp<Calculator>() {
            @Mock
            public int add(int a, int b) {
                return -1;
            }
        };

        System.out.println("mock 之后");
        System.out.println(calculator.add(1, 2));
    }

    @Test
    public void test_add_02() {
        System.out.println("--test_add_02--");
        Calculator calculator = new Calculator();
        System.out.println(calculator.add(1, 2));
    }

}

执行 CalculatorTest 中的单测,test_add_01 先执行,test_add_02 后执行,如果 test_add_02 输出 -1,那么意味着 JMockit 在当前的测试框架无法自动 tear down。


( 本文完 )