You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用JUnit和Mockito为调用JNA原生库的ClassB编写单元测试

解决方案

你面临的核心问题是InterfaceA中的INSTANCE是静态final常量,默认Mockito无法直接替换,下面给出两种可行方案:

方案一:重构原有代码(优先推荐)

这是最符合可测试性设计的方案,仅需要对ClassB做少量修改,不需要引入额外测试依赖或复杂操作:

  1. 为ClassB增加构造函数,将InterfaceA作为依赖注入,保留无参构造函数兼容原有业务逻辑
    重构后的代码如下:
package x.x.x;
import com.sun.jna.Library;
import com.sun.jna.Native;

public class ClassB {
    
    public interface InterfaceA extends Library {
        InterfaceA INSTANCE = (InterfaceA) Native.load("ClassA.api", InterfaceA.class);
        int interfaceMethod1();
        int interfaceMethod2(String string1);
    }    

    private final InterfaceA interfaceA;

    // 原有无参构造函数,兼容现有业务代码调用逻辑
    public ClassB() {
        this.interfaceA = InterfaceA.INSTANCE;
    }

    // 供测试用的构造函数,可传入mock实例
    public ClassB(InterfaceA interfaceA) {
        this.interfaceA = interfaceA;
    }

    public boolean method1(){
        int result = interfaceA.interfaceMethod1();
        return result != 0;
    }

    public boolean method2(String string1){
        int result = interfaceA.interfaceMethod2(string1);
        return result != 0;
    }
}

对应的单元测试代码(JUnit 5 + Mockito):

package x.x.x;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

class ClassBTest {

    @Test
    void testMethod1() {
        // mock InterfaceA实例
        InterfaceA mockInterfaceA = Mockito.mock(InterfaceA.class);
        // 定义mock方法返回值
        when(mockInterfaceA.interfaceMethod1()).thenReturn(1);
        // 传入mock实例构造ClassB
        ClassB classB = new ClassB(mockInterfaceA);
        // 执行测试断言
        assertTrue(classB.method1());
        // 验证方法被调用
        Mockito.verify(mockInterfaceA).interfaceMethod1();
    }

    @Test
    void testMethod2() {
        InterfaceA mockInterfaceA = Mockito.mock(InterfaceA.class);
        when(mockInterfaceA.interfaceMethod2("test")).thenReturn(0);
        ClassB classB = new ClassB(mockInterfaceA);
        assertFalse(classB.method2("test"));
    }
}

方案二:不修改原有代码,反射替换静态常量

如果无法修改原有业务代码,可以通过反射临时替换InterfaceA中的INSTANCE常量为mock对象,测试完成后恢复原值避免影响其他用例。
需要注意:JDK 16及以上版本需要添加JVM启动参数 --add-opens java.base/java.lang.reflect=ALL-UNNAMED 才能修改final字段。

测试代码示例:

package x.x.x;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

class ClassBTest {
    private Field instanceField;
    private InterfaceA originInstance;
    private InterfaceA mockInterfaceA;

    @BeforeEach
    void setUp() throws NoSuchFieldException, IllegalAccessException {
        // 构造mock实例
        mockInterfaceA = Mockito.mock(InterfaceA.class);
        // 获取INSTANCE字段
        instanceField = InterfaceA.class.getDeclaredField("INSTANCE");
        instanceField.setAccessible(true);
        // 移除final修饰符
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(instanceField, instanceField.getModifiers() & ~Modifier.FINAL);
        // 保存原有实例
        originInstance = (InterfaceA) instanceField.get(null);
        // 替换为mock实例
        instanceField.set(null, mockInterfaceA);
    }

    @AfterEach
    void tearDown() throws IllegalAccessException {
        // 恢复原有实例,避免影响其他测试用例
        instanceField.set(null, originInstance);
    }

    @Test
    void testMethod1() {
        when(mockInterfaceA.interfaceMethod1()).thenReturn(1);
        ClassB classB = new ClassB();
        assertTrue(classB.method1());
    }

    @Test
    void testMethod2() {
        when(mockInterfaceA.interfaceMethod2("testStr")).thenReturn(0);
        ClassB classB = new ClassB();
        assertFalse(classB.method2("testStr"));
    }
}

补充:也可使用PowerMock框架通过字节码修改替换静态常量,本质和反射方案逻辑一致,但PowerMock对高版本JDK、JUnit 5的兼容性较差,非必要不推荐使用。

内容的提问来源于stack exchange,提问作者user344563

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 03:42:00