如何通过XUnit Mock测试在Redis缓存中存储对象?
Redis缓存单元测试:正确配置Mock实现对象存储与读取
问题场景
在为自定义Redis缓存操作类CacheAccessor编写单元测试时,尝试MockIDatabase模拟Redis存储Dog对象,但调用GetAsync时返回的是类名{AppServices.Models.Dog},而非预期的带属性的对象,且出现解析错误。核心问题在于Mock时用了pet.ToString(),而实际Redis存储的是JSON序列化后的字符串,与CacheAccessor中用JsonConvert.DeserializeObject反序列化的逻辑不匹配。
现有测试代码
Dog pet = new Dog { name = "Jon", id = "1234" }; var fakeDB = new Mock<IDatabase>(); var cacheAccessor = new CacheAccessor(fakeDB.Object); // 错误的Mock配置:返回ToString()结果而非JSON fakeDB.Setup(t => t.StringGetAsync(It.IsAny<RedisKey>(), It.IsAny<CommandFlags>())) .Returns(Task.Run<RedisValue>(() => pet.ToString())); var response = await cacheAccessor.GetAsync(key); // 解析错误,返回类名字符串
CacheAccessor的GetAsync方法
public async Task<Pet> GetAsync(string key, Type petType = null) { var res = await this.cacheDatabase.StringGetAsync(key).ConfigureAwait(false); // 代码存在变量名不一致问题(res vs responseText),需修正 var deserializedResponse = JsonConvert.DeserializeObject(responseText, petType); return deserializedResponse != null ? (Pet)deserializedResponse : null; }
解决方案
1. 修正Mock的返回值为JSON序列化字符串
因为CacheAccessor用JSON反序列化,所以Mock的StringGetAsync必须返回对象序列化后的JSON字符串,而非默认的ToString()结果。
修改后的测试代码:
Dog pet = new Dog { name = "Jon", id = "1234" }; var fakeDB = new Mock<IDatabase>(); var cacheAccessor = new CacheAccessor(fakeDB.Object); // 正确Mock:返回序列化后的JSON字符串 var serializedPet = JsonConvert.SerializeObject(pet); fakeDB.Setup(t => t.StringGetAsync(It.IsAny<RedisKey>(), It.IsAny<CommandFlags>())) .ReturnsAsync(new RedisValue(serializedPet)); var response = await cacheAccessor.GetAsync(key, typeof(Dog)); // 此时response应为包含name和id属性的Dog对象
2. 修正CacheAccessor中的变量名错误
原GetAsync方法中,获取到的res变量未被使用,反而引用了未定义的responseText,这会导致运行时错误,需修正为:
public async Task<Pet> GetAsync(string key, Type petType = null) { var res = await this.cacheDatabase.StringGetAsync(key).ConfigureAwait(false); var deserializedResponse = JsonConvert.DeserializeObject(res.ToString(), petType); return deserializedResponse != null ? (Pet)deserializedResponse : null; }
3. 可选:验证Mock方法的调用(增强测试严谨性)
如果需要确保StringGetAsync被正确调用,可以在测试末尾添加验证:
fakeDB.Verify(t => t.StringGetAsync(It.Is<RedisKey>(k => k == key), It.IsAny<CommandFlags>()), Times.Once);
内容的提问来源于stack exchange,提问作者HackerMan33453
相关产品推荐
相关产品推荐

