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

如何为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 00:45:02