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

Mockito匹配Spring ReactiveRedisTemplate重载方法可变参数的问题求助

问题:Mock ReactiveRedisTemplate的delete可变参数方法失败

服务中的deleteObject方法

服务中实现了基于ReactiveRedisTemplate的批量删除方法:

private final ReactiveRedisTemplate<String, String> reactiveRedisTemplate;

public Mono<Boolean> deleteObject(String... keys) {
    return reactiveRedisTemplate.delete(keys).map(c -> c > 0);
}

ReactiveRedisTemplate的delete重载实现

ReactiveRedisTemplate内部有两个签名不同的delete方法:

@SafeVarargs
public final Mono<Long> delete(K... keys) {
    Assert.notNull(keys, "Keys must not be null!");
    Assert.notEmpty(keys, "Keys must not be empty!");
    Assert.noNullElements(keys, "Keys must not contain null elements!");
    if (keys.length == 1) {
        // 省略实现
    }
    // 省略实现
}

public Mono<Long> delete(Publisher<K> keys) {
    Assert.notNull(keys, "Keys must not be null!");
    return this.doCreateFlux((connection) -> {
        // 省略实现
    });
}

测试代码及初始报错

执行以下测试代码时出现NullPointerException,推测是可变参数匹配失败:

var key = "key";
when(reactiveRedisTemplate.delete(key)).thenReturn(Mono.just(1L));
StepVerifier.create(service.deleteObject(key))
            .expectNextMatches(c -> c)
            .verifyComplete();
verify(this.reactiveRedisTemplate, times(1)).delete(key);

尝试的匹配器及报错

尝试多种ArgumentMatcher方式均报错:

  • 使用any()匹配:

    when(reactiveRedisTemplate.delete(ArgumentMatchers.<String>any())).thenReturn(Mono.just(1L));
    

    报错:java.lang.IllegalArgumentException: Keys must not contain null elements!

  • 使用eq()匹配数组:

    when(reactiveRedisTemplate.delete(eq(new String[] { key }))).thenReturn(Mono.just(1L));
    

    报错:java.lang.IllegalArgumentException: Keys must not be null!

  • 使用anyString()匹配:

    when(reactiveRedisTemplate.delete(anyString())).thenReturn(Mono.just(1L));
    

    报错:java.lang.NullPointerException(匹配失败)

  • 自定义可变参数匹配器:

    public class StringVarargsMatcher implements VarargMatcher, ArgumentMatcher<String[]> {
        private String[] expectedValues;
    
        public StringVarargsMatcher(String... expectedValues) {
            Arrays.sort(expectedValues);
            this.expectedValues = expectedValues;
        }
    
        @Override
        public boolean matches(String[] strings) {
            Arrays.sort(strings);
            return Arrays.equals(strings, expectedValues);
        }
    }
    
    when(reactiveRedisTemplate.delete(argThat(new StringVarargsMatcher(key)))).thenReturn(Mono.just(1L));
    

    报错:java.lang.IllegalArgumentException: Keys must not be null!


解决方案

核心问题是:使用when(mock.method(...))时,Mockito会调用真实的方法逻辑(这里是ReactiveRedisTemplate的delete方法),触发了内部的Assert断言检查,导致报错。改用doReturn可以绕过真实方法的执行,直接指定返回值。

方法1:用doReturn绕过断言

var key = "key";
// 方式1:匹配任意String数组参数
doReturn(Mono.just(1L)).when(reactiveRedisTemplate).delete(any(String[].class));

// 方式2:精确匹配指定参数数组
// doReturn(Mono.just(1L)).when(reactiveRedisTemplate).delete(eq(new String[]{key}));

StepVerifier.create(service.deleteObject(key))
            .expectNextMatches(c -> c)
            .verifyComplete();

// 验证时同样用数组匹配
verify(reactiveRedisTemplate, times(1)).delete(eq(new String[]{key}));

方法2:用varargThat精确匹配(Mockito 3+)

如果需要更灵活的参数匹配,结合doReturn使用varargThat:

var key = "key";
doReturn(Mono.just(1L)).when(reactiveRedisTemplate)
    .delete(varargThat(keys -> Arrays.equals(keys, new String[]{key})));

StepVerifier.create(service.deleteObject(key))
            .expectNextMatches(c -> c)
            .verifyComplete();

verify(reactiveRedisTemplate, times(1))
    .delete(varargThat(keys -> Arrays.equals(keys, new String[]{key})));

原理说明

doReturn是Mockito的“存根”方式,它不会调用mock对象的真实方法,只是告诉Mockito当指定方法被调用时返回什么值。而when(mock.method(...))会先调用一次真实方法来获取方法引用,这就触发了ReactiveRedisTemplate中delete方法的参数断言,导致各种IllegalArgumentException或NullPointerException。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 14:15:31