如何使用NSubstitute为调用ASP.NET Core IMemoryCache的方法配置单元测试Mock
问题根因
GetOrCreateAsync是IMemoryCache的扩展方法,不属于接口原生定义,NSubstitute无法直接Mock扩展方法,需要对扩展方法底层调用的IMemoryCache原生方法、以及依赖的ICacheEntry实例进行完整配置,才不会出现返回值类型不匹配、CacheEntry为null的报错。
正确Mock配置步骤
1. 模拟ICacheEntry实例
先构造符合要求的ICacheEntry模拟对象,覆盖所有缓存操作会用到的成员:
var mockCacheEntry = Substitute.For<ICacheEntry>(); // 配置Key属性 mockCacheEntry.Key.Returns("对应缓存key"); // 配置绝对过期设置所需属性 mockCacheEntry.AbsoluteExpirationRelativeToNow.Returns(TimeSpan.FromMinutes(5)); // 配置Dispose方法无异常 mockCacheEntry.When(x => x.Dispose()).Do(_ => { });
2. 配置IMemoryCache核心原生方法
GetOrCreateAsync底层会依次调用TryGetValue、CreateEntry,按需选择配置方案即可:
方案A:直接返回缓存值,跳过缓存工厂逻辑(适合不需要测试缓存工厂的场景)
var mockMemoryCache = Substitute.For<IMemoryCache>(); // 配置缓存命中,直接返回预设的DataEntity object expectedData = new DataEntity { Id = "id" }; mockMemoryCache.TryGetValue(Arg.Any<object>(), out expectedData).Returns(true);
方案B:允许走缓存工厂逻辑(适合需要测试缓存工厂内部逻辑的场景)
var mockMemoryCache = Substitute.For<IMemoryCache>(); // 配置缓存未命中,走工厂逻辑 object cacheValue = null; mockMemoryCache.TryGetValue(Arg.Any<object>(), out cacheValue).Returns(false); // 配置CreateEntry返回我们模拟的ICacheEntry mockMemoryCache.CreateEntry(Arg.Any<object>()).Returns(mockCacheEntry);
3. 修正测试代码的其他问题
现有测试代码还存在以下错误需要同步修正:
- 业务方法中调用的是
GetInfo而非GetUser,不需要配置service.GetUser的返回,需要对应配置GetInfo的依赖,或是直接MockGetInfo的返回值 Act阶段调用UpdateWeight的代码少了右括号- 业务方法中未定义
newWeight变量,需要对齐参数名
完整可运行测试示例
public async Task UpdateWeight_WhenDataExists_ReturnSuccessResult() { // Arrange var id = "id"; int inputWeight = 30; var mockData = new DataEntity { Id = id, UserId = "user1" }; var mockUser = new User { UserId = "user1" }; // 配置缓存Mock var mockCacheEntry = Substitute.For<ICacheEntry>(); mockCacheEntry.When(x => x.Dispose()).Do(_ => { }); object cacheVal = null; var mockMemoryCache = Substitute.For<IMemoryCache>(); mockMemoryCache.TryGetValue(Arg.Any<object>(), out cacheVal).Returns(false); mockMemoryCache.CreateEntry(Arg.Any<object>()).Returns(mockCacheEntry); // 注入模拟缓存到你的服务实例 var yourService = new YourService(mockMemoryCache /* 其他依赖参数 */); // 配置服务内部依赖的方法返回 yourService.GetDataFromDb(Arg.Any<string>()).Returns(mockData); yourService.GetInfo(Arg.Any<string>()).Returns(mockUser); yourService.UpdateWeight(Arg.Any<int>(), Arg.Any<User>()).Returns(true); // Act var result = await yourService.UpdateWeight(id, inputWeight); // Assert result.IsSuccessful.Should().BeTrue(); result.Weight.Should().Be(inputWeight); }
内容的提问来源于stack exchange,提问作者Noelia
相关产品推荐
相关产品推荐

