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

Mockito如何区分两个不同泛型类型方法的when()模拟?

解决Mockito因泛型类型擦除无法区分Generic<T,R>方法调用的问题

这个问题确实是Java泛型类型擦除带来的经典Mockito痛点——编译时的泛型信息到运行时就被擦除了,Mockito默认只能识别到Generic这个原始类型,所以会把两个调用当成同一个来处理。下面给你几个实用的解决方案,你可以根据自己的场景选择:

1. 自定义ArgumentMatcher(最常用)

你可以写一个自定义的参数匹配器,专门用来区分不同泛型参数的Generic对象。核心思路是:要么利用Generic类本身提供的类型信息(比如保存了泛型对应的Class对象),要么通过反射获取泛型参数(仅限特定场景)。

示例代码:

首先定义匹配器:

import org.mockito.ArgumentMatcher;

class GenericTypeMatcher<T, R> implements ArgumentMatcher<Generic<T, R>> {
    private final Class<T> expectedTypeT;
    private final Class<R> expectedTypeR;

    public GenericTypeMatcher(Class<T> expectedTypeT, Class<R> expectedTypeR) {
        this.expectedTypeT = expectedTypeT;
        this.expectedTypeR = expectedTypeR;
    }

    @Override
    public boolean matches(Object argument) {
        if (!(argument instanceof Generic)) {
            return false;
        }

        Generic<?, ?> generic = (Generic<?, ?>) argument;
        // 情况1:如果Generic类本身保存了泛型的Class对象(推荐提前改造Generic类)
        return expectedTypeT.equals(generic.getTClass()) && expectedTypeR.equals(generic.getRClass());

        // 情况2:如果无法修改Generic类,仅当参数是匿名内部类实例时可用(反射获取泛型)
        // Type superType = argument.getClass().getGenericSuperclass();
        // if (superType instanceof ParameterizedType) {
        //     ParameterizedType paramType = (ParameterizedType) superType;
        //     Type[] typeArgs = paramType.getActualTypeArguments();
        //     return typeArgs[0].equals(expectedTypeT) && typeArgs[1].equals(expectedTypeR);
        // }
        // return false;
    }
}

然后在测试中使用:

import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.when;

// ...

when(someObject.someMethod(argThat(new GenericTypeMatcher<>(TypeA.class, TypeB.class))))
    .thenReturn(responseOne());

when(someObject.someMethod(argThat(new GenericTypeMatcher<>(TypeB.class, TypeC.class))))
    .thenReturn(responseTwo());

2. 使用Mockito的Answer动态判断

如果不想写单独的匹配器,你可以用Answer接口在运行时动态检查传入的参数,再返回对应的响应:

import static org.mockito.Mockito.when;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

// ...

when(someObject.someMethod(any(Generic.class))).thenAnswer(new Answer<Object>() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        Generic<?, ?> inputGeneric = invocation.getArgument(0);
        // 同样需要根据Generic的类型信息判断
        if (inputGeneric.getTClass().equals(TypeA.class) && inputGeneric.getRClass().equals(TypeB.class)) {
            return responseOne();
        } else if (inputGeneric.getTClass().equals(TypeB.class) && inputGeneric.getRClass().equals(TypeC.class)) {
            return responseTwo();
        }
        // 处理意外情况
        throw new IllegalArgumentException("Unexpected Generic type");
    }
});

3. 提前改造Generic类(最优解)

如果你的项目允许修改Generic类,最稳妥的方式是在类中保存泛型对应的Class对象,这样不管运行时怎么擦除,都能直接拿到类型信息:

public class Generic<T, R> {
    private final Class<T> tClass;
    private final Class<R> rClass;

    // 构造函数传入Class对象
    public Generic(Class<T> tClass, Class<R> rClass) {
        this.tClass = tClass;
        this.rClass = rClass;
    }

    // 提供getter方法
    public Class<T> getTClass() {
        return tClass;
    }

    public Class<R> getRClass() {
        return rClass;
    }

    // 其他原有逻辑...
}

这样上面的匹配器和Answer都能稳定工作,不用依赖反射的特殊场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:10:45