How to mock HashOperations of Spring StringRedisTemplate?
I'm writing unit test for my CacheService
class:
public class CacheService {
private final HashOperations<String, String, String> redisHashOps;
private final ValueOperations<String, String> valueOps;
private final SetOperations<String, String> setOps;
public CacheService(StringRedisTemplate redisTemplate) {
this.redisHashOps = redisTemplate.opsForHash();
this.valueOps = redisTemplate.opsForValue();
this.setOps = redisTemplate.opsForSet();
}
Unit test:
public class CacheServiceTest {
private CacheService cacheService;
private HashOperations<String, String, String> redisHashOps;
private ValueOperations<String, String> redisValueOps;
private SetOperations<String, String> redisSetOps;
@BeforeEach
public void setUp() {
final StringRedisTemplate redisTemplate = mock(StringRedisTemplate.class);
redisValueOps = mock(ValueOperations.class);
redisHashOps = mock(HashOperations.class);
redisSetOps = mock(SetOperations.class);
when(redisTemplate.opsForValue()).thenReturn(redisValueOps);
when(redisTemplate.opsForHash()).thenReturn(redisHashOps);
when(redisTemplate.opsForSet()).thenReturn(redisSetOps);
redisValueOps
and redisSetOps
have no problem, but for redisHashOps
, the IDE prompts:
If I build the project will see following error:
java: no suitable method found for thenReturn(org.springframework.data.redis.core.HashOperations<java.lang.String,java.lang.String,java.lang.String>)
method org.mockito.stubbing.OngoingStubbing.thenReturn(org.springframework.data.redis.core.HashOperations<java.lang.String,java.lang.Object,java.lang.Object>) is not applicable
(argument mismatch; org.springframework.data.redis.core.HashOperations<java.lang.String,java.lang.String,java.lang.String> cannot be converted to org.springframework.data.redis.core.HashOperations<java.lang.String,java.lang.Object,java.lang.Object>)
method org.mockito.stubbing.OngoingStubbing.thenReturn(org.springframework.data.redis.core.HashOperations<java.lang.String,java.lang.Object,java.lang.Object>,org.springframework.data.redis.core.HashOperations<java.lang.String,java.lang.Object,java.lang.Object>...) is not applicable
(argument mismatch; org.springframework.data.redis.core.HashOperations<java.lang.String,java.lang.String,java.lang.String> cannot be converted to org.springframework.data.redis.core.HashOperations<java.lang.String,java.lang.Object,java.lang.Object>)
Looking at RedisTemplate's source code, I found the difference between opsForHash and opsForValue:
public <HK, HV> HashOperations<K, HK, HV> opsForHash() ...
public ValueOperations<K, V> opsForValue() ...
How to modify the unit test to make it working?
Solution 1:
You can rewrite your mock expectations using doReturn
instead:
doReturn(redisHashOps).when(redisTemplate).opsForHash();
Solution 2:
You found it right:
In the first (and last) case <K, V>
refers to RedisTemplate
generic parameters (which are assigned to <String, String>
here).
In case of <HK, HV> HashOperations<K, HK, HV>
, the K
is "safe again", but <HK, HV>
are "method generic parameters", which the compiler cannot infer here.
Solutions:
- as armandino correctly proposed with:
we free the compiler from guessing.doReturn(redisHashOps).when(redisTemplate).opsForHash();
- or by passing the "generic arguments":
when(redisTemplate.<String,String>opsForHash()).thenReturn(redisHashOps);
- or the "raw"/brutal approach:
private HashOperations<String, Object, Object> redisHashOps; ... when(redisTemplate.opsForHash()).thenReturn(redisHashOps);