如何为C# MediatR处理器编写单元测试?现有测试求优化建议
单元测试优化建议
1. 剥离不必要的依赖,保证测试职责单一
缓存命中场景的逻辑完全不会触发EF Core数据库查询,当前测试初始化InMemory数据库属于冗余操作,既拖慢测试执行速度,也可能引入非相关依赖的干扰。该场景下甚至可以直接Mock MyContext 无需初始化真实数据库,只需要验证数据库查询逻辑未被调用即可。
2. 补充行为验证,不止验证返回结果
当前测试仅验证了返回值的正确性,缺失对代码执行路径的行为校验,建议补充以下验证:
- 缓存命中场景:
- 验证
ExistsAsync、PullAsync均以请求的key作为参数调用1次 - 验证
PutAsync从未被调用 - 验证数据库查询逻辑从未执行
- 除了校验
ConfigKey,补充校验ConfigValue和预期一致
- 验证
- 缓存未命中场景:
- 验证
ExistsAsync以请求的key调用1次 - 验证
PullAsync从未被调用 - 验证
PutAsync被调用1次,且传入的缓存key、值、过期时间均和预期配置一致 - 同样补充
ConfigValue的校验
- 验证
示例验证代码:
// 缓存命中场景验证PutAsync未调用 cacheProviderMock.Verify(x => x.PutAsync(It.IsAny<CacheItem<ConfigResponse>>(), It.IsAny<CacheOptions>()), Times.Never); // 缓存未命中场景验证PutAsync参数正确 cacheProviderMock.Verify(x => x.PutAsync( It.Is<CacheItem<ConfigResponse>>(c => c.Key == "key2" && c.Value.ConfigValue == "value2"), It.Is<CacheOptions>(o => o.ExpireAfter == TimeSpan.FromMinutes(3).TotalMilliseconds)), Times.Once);
3. 避免测试用例状态污染
- 当前全局复用同一个
cacheProviderMock实例,前一个测试的Setup会影响后一个测试的执行,建议每个测试单独实例化Mock对象,或者每次测试执行前调用cacheProviderMock.Reset()重置状态 - InMemory数据库使用固定名称会导致多个测试共享同一份数据,建议将数据库名设置为随机值,比如
Guid.NewGuid().ToString(),保证每个测试的数据库实例完全隔离。
4. 补充缺失的边界场景
当前仅覆盖了两个正常流程,还需要补充以下场景的测试:
- 缓存不存在,且数据库中也查询不到对应key的场景,验证返回
null,且PutAsync不会被调用 cacheConfigs为null的容错场景,避免空引用异常- 可选:取消令牌生效时,EF查询能正确响应中断的场景
5. 复用重复代码,降低维护成本
当前两个测试中初始化InMemory数据库、构造Handler的逻辑完全重复,可以抽为公共私有方法,后续修改依赖构造逻辑时无需多处修改。
可选优化:完全隔离外部依赖
如果需要严格的单元测试(不依赖任何外部组件包括内存数据库),可以直接Mock DbSet<Config> 替代InMemory数据库,测试执行速度会更快,也完全排除数据库层面的干扰。
内容的提问来源于stack exchange,提问作者happy-integer
相关产品推荐
相关产品推荐

