如何Mock Configuration.GetSection("foo:bar").Get<List<string>>()进行单元测试
Mocking Configuration.GetSection for List in Unit Tests
我之前也踩过这个坑!Moq没办法直接Setup Get<List<string>>()这种扩展方法——因为它不是IConfiguration或IConfigurationSection接口的原生成员,直接写你原来的语法肯定会失败。下面给你两种可行的解决方案,按需选择:
方案1:Mock IConfigurationSection的子节点结构
既然Get<List<string>>()扩展方法是通过遍历配置节的子节点来构建列表的,那我们就Mock出对应的子节点结构:
// 准备要返回的测试列表 var reportLanguages = new List<string> { "en", "fr", "de" }; // 为列表中的每个元素创建Mock的子配置节 var mockChildSections = reportLanguages.Select((lang, index) => { var childSection = new Mock<IConfigurationSection>(); childSection.Setup(s => s.Key).Returns(index.ToString()); // 列表项的索引作为Key childSection.Setup(s => s.Value).Returns(lang); // 列表项的值 return childSection.Object; }).ToList(); // Mock目标配置节,让它返回上面创建的子节点 var mockTargetSection = new Mock<IConfigurationSection>(); mockTargetSection.Setup(s => s.GetChildren()).Returns(mockChildSections); // 最后Mock IConfiguration的GetSection方法,返回我们准备好的配置节 var mockConfigRepo = new Mock<IConfiguration>(); mockConfigRepo.Setup(x => x.GetSection("reportLanguageSettings:reportLanguageList")) .Returns(mockTargetSection.Object);
这样当你调用Configuration.GetSection("...").Get<List<string>>()时,扩展方法会遍历我们Mock的子节点,最终得到预期的列表。
方案2:直接构建真实的内存配置(更推荐)
如果不需要严格Mock IConfiguration,直接用ConfigurationBuilder创建一个内存中的真实配置实例会更简单,而且测试逻辑和生产环境完全一致:
var reportLanguages = new List<string> { "en", "fr", "de" }; // 用内存集合构建配置,模拟config.json中的结构 var config = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary<string, string> { {"reportLanguageSettings:reportLanguageList:0", "en"}, {"reportLanguageSettings:reportLanguageList:1", "fr"}, {"reportLanguageSettings:reportLanguageList:2", "de"} }) .Build(); // 直接把这个config注入到你要测试的类中即可,不需要Mock
这种方法避开了Moq对扩展方法的限制,代码更简洁,测试结果也更可靠,我自己写单元测试时大多用这种方式。
内容的提问来源于stack exchange,提问作者Engineer
相关产品推荐
相关产品推荐

