1. 概述

在 Java 单元测试中,我们经常使用主流的 Mock 框架,比如 Mockito 和 EasyMock。这些框架的底层原理通常是基于 Java 的继承机制:
EasyMock 是通过运行时动态实现接口来创建 Mock 对象
Mockito 则是通过继承目标类生成子类来实现方法拦截

⚠️ 但这种方式对静态方法无效 —— 因为静态方法属于类本身,无法被重写(override),自然也无法通过继承来拦截。

JMockit 是个例外,它基于 Java Agent 字节码增强技术,能够直接修改类的字节码,因此可以支持对静态方法、构造函数、私有方法等进行 Mock。

本文将演示如何使用 JMockit 来 Mock 静态方法。

📌 如果你还不熟悉 JMockit 的基本用法,建议先阅读我们的 JMockit 入门指南


2. Maven 依赖

使用 JMockit 前,先引入对应的依赖:

<dependency>
    <groupId>org.jmockit</groupId>
    <artifactId>jmockit</artifactId>
    <version>1.49</version>
    <scope>test</scope>
</dependency>

📌 最新版本可查看 Maven Central

⚠️ 注意:JMockit 依赖 Java Agent 机制,如果你使用的是 JUnit 5,建议在 src/test/resources/jmockit-coverage.properties 中配置 agent,或通过 IDE 启用 -javaagent 参数(具体配置可参考官方文档)。


3. 非静态方法中调用静态方法的场景

考虑这样一个类:

public class AppManager {

    public boolean managerResponse(String question) {
        return AppManager.isResponsePositive(question);
    }

    public static boolean isResponsePositive(String value) {
        if (value == null) {
            return false;
        }
        int length = value.length();
        int randomNumber = randomNumber();
        return length == randomNumber ? true : false;
    }

    private static int randomNumber() {
        return new Random().nextInt(7);
    }
}

我们想测试 managerResponse() 方法,但它的返回值依赖于静态方法 isResponsePositive(),而这个方法内部还调用了 randomNumber() —— 返回值不可控 ❌。

踩坑提醒

如果不用 JMockit,这类方法几乎无法可靠测试。Mockito 直接 Mock 静态方法要等到 3.4.0 版本以后(通过 mockStatic),而且语法更复杂。而 JMockit 的方案更早且更灵活。

解决方案:使用 MockUp<T> + @Mock

JMockit 提供了 MockUp<T> 泛型抽象类,配合 @Mock 注解,可以直接 Mock 目标类的所有静态方法:

@Test
public void givenAppManager_whenStaticMethodCalled_thenValidateExpectedResponse() {
    new MockUp<AppManager>() {
        @Mock
        public boolean isResponsePositive(String value) {
            return false;  // 强制返回 false,绕过随机逻辑
        }
    };

    AppManager appManager = new AppManager();
    assertFalse(appManager.managerResponse("Some string..."));
}

✅ 解读:

  • new MockUp<AppManager>() 表示我们要对 AppManager 类进行 Mock
  • @Mock 注解的方法签名必须和原方法一致(包括 static 修饰符可省略)
  • 所有对该静态方法的调用都会被重定向到这个 Mock 实现

📌 这种方式简单粗暴,直接“替换”了原方法的实现,非常适合隔离外部依赖或不可控逻辑。


4. 总结

本文展示了如何使用 JMockit 来 Mock 静态方法,解决了传统 Mock 框架难以处理的痛点。

✅ 核心优势:

  • 支持静态、私有、构造方法等传统 Mock 工具无法覆盖的场景
  • API 简洁,通过 MockUp<T> + @Mock 即可完成替换
  • 基于字节码增强,不依赖继承,适用范围广

⚠️ 注意事项:

  • 需要启用 Java Agent(尤其是 JUnit 5 环境下)
  • 过度使用可能导致测试“太假”,建议仅用于难以解耦的遗留代码或工具类

更多高级用法,如部分 Mock、Expectations API、Mock 构造函数等,欢迎阅读我们的 JMockit 高级用法指南

🔗 完整示例代码已托管至 GitHub:https://github.com/eugenp/tutorials/tree/master/testing-modules/mocks


原始标题:Mock Static Method using JMockit