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

无法模拟接收Supplier作为入参的静态方法问题求助

问题

业务代码中调用了RemoteApis的静态方法,流程如下:

var saasManagerApi = RemoteApis.saasSubscriptionApi(saasRegistryServiceUrl, cisLocalBindingToken);
var results = Jsons.parser(RemoteApis.call(saasManagerApi::getApplications))
        .read("$.applications[?(@.appId =~ /^fos-multitenant-svc.*/)]");
var appName = Jsons.parser(results).read("$[0].appName", String.class);
var state = saasManagerV2Service.retrieveSubState(appName, saasManagerApi);

测试时尝试mockRemoteApis静态类,代码如下:

try (MockedStatic<RemoteApis> remoteApisMockedStatic = mockStatic(RemoteApis.class);) {
    var mockSaasManagerApi = mock(SaasManagerApi.class);
    remoteApisMockedStatic.when(() -> RemoteApis.saasSubscriptionApi(any(), any())).thenReturn(mockSaasManagerApi);
    remoteApisMockedStatic.when(() -> RemoteApis.call(mockSaasManagerApi::getApplications))
          .thenReturn("Some mocked JSON Response");
}

但测试时RemoteApis.call返回null,换成any()匹配所有参数才能得到预期结果:

remoteApisMockedStatic.when(() -> RemoteApis.call(any()))
              .thenReturn("Some mocked JSON Response");

由于需要区分不同的Supplier参数(对应不同的API方法调用),不能用any()通配,需要解决精准匹配的问题。

原因

直接使用mockSaasManagerApi::getApplications作为匹配参数时,Mockito会按对象相等性判断参数是否匹配。但业务代码中生成的saasManagerApi::getApplications是一个新的Supplier实例,和测试代码中写的mockSaasManagerApi::getApplications是两个不同的对象,因此匹配失败,返回默认的null。

解决方案

方案1:自定义参数匹配器,验证Supplier的行为

通过argThat传入自定义匹配器,判断传入的Supplier是否对应目标方法的调用:

try (MockedStatic<RemoteApis> remoteApisMockedStatic = mockStatic(RemoteApis.class)) {
    SaasManagerApi mockSaasManagerApi = mock(SaasManagerApi.class);
    // 先mock目标方法的返回,避免执行Supplier时抛出异常
    when(mockSaasManagerApi.getApplications()).thenReturn(new Object());
    
    remoteApisMockedStatic.when(() -> RemoteApis.saasSubscriptionApi(any(), any()))
                          .thenReturn(mockSaasManagerApi);

    // 自定义匹配器:执行Supplier,验证是否调用了getApplications方法
    remoteApisMockedStatic.when(() -> RemoteApis.call(argThat(supplier -> {
        try {
            supplier.get();
            verify(mockSaasManagerApi).getApplications();
            return true;
        } catch (Exception e) {
            return false;
        }
    }))).thenReturn("Some mocked JSON Response");
}

方案2:用thenAnswer动态判断参数

通过thenAnswer拦截所有RemoteApis.call调用,根据传入的Supplier执行逻辑返回对应结果:

try (MockedStatic<RemoteApis> remoteApisMockedStatic = mockStatic(RemoteApis.class)) {
    SaasManagerApi mockSaasManagerApi = mock(SaasManagerApi.class);
    when(mockSaasManagerApi.getApplications()).thenReturn(new Object());
    
    remoteApisMockedStatic.when(() -> RemoteApis.saasSubscriptionApi(any(), any()))
                          .thenReturn(mockSaasManagerApi);

    remoteApisMockedStatic.when(() -> RemoteApis.call(any())).thenAnswer(invocation -> {
        Supplier<?> supplier = invocation.getArgument(0);
        try {
            supplier.get();
            // 验证是否调用了目标方法
            verify(mockSaasManagerApi).getApplications();
            return "Some mocked JSON Response";
        } catch (VerificationError e) {
            // 针对其他Supplier返回对应mock结果
            return "Other mocked response";
        }
    });
}

方案3:利用反射匹配方法引用(进阶)

如果需要更精准的匹配,可以通过反射获取Supplier对应的方法信息:

remoteApisMockedStatic.when(() -> RemoteApis.call(argThat(supplier -> {
    // 获取Supplier的target和method
    Field targetField = supplier.getClass().getDeclaredField("target");
    targetField.setAccessible(true);
    Object target = targetField.get(supplier);
    
    Field methodField = supplier.getClass().getDeclaredField("method");
    methodField.setAccessible(true);
    Method method = (Method) methodField.get(supplier);
    
    return target == mockSaasManagerApi && method.getName().equals("getApplications");
}))).thenReturn("Some mocked JSON Response");

注意:这种方式依赖于方法引用的底层实现(不同JDK版本可能有差异),建议优先使用前两种方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 14:40:55