如何模拟 IConfiguration.GetValue

问题描述 投票:0回答:10

我徒劳地尝试模拟顶级(不属于任何部分)配置值(.NET Core 的 IConfiguration)。例如,这些都不起作用(使用 NSubstitute,但它与 Moq 或我相信的任何模拟包相同):

var config = Substitute.For<IConfiguration>();
config.GetValue<string>(Arg.Any<string>()).Returns("TopLevelValue");
config.GetValue<string>("TopLevelKey").Should().Be("TopLevelValue"); // nope
// non generic overload
config.GetValue(typeof(string), Arg.Any<string>()).Returns("TopLevelValue");
config.GetValue(typeof(string), "TopLevelKey").Should().Be("TopLevelValue"); // nope

就我而言,我还需要从同一个配置实例调用

GetSection

c# .net-core mocking nsubstitute
10个回答
281
投票

您可以使用带有内存数据的实际配置实例。

//Arrange
var inMemorySettings = new Dictionary<string, string> {
    {"TopLevelKey", "TopLevelValue"},
    {"SectionName:SomeKey", "SectionValue"},
    //...populate as needed for the test
};

IConfiguration configuration = new ConfigurationBuilder()
    .AddInMemoryCollection(inMemorySettings)
    .Build();


//...

现在需要使用所需的配置来进行测试

//...

string value = configuration.GetValue<string>("TopLevelKey");

string sectionValue = configuration.GetSection("SectionName").GetValue<string>("SomeKey");

//...

参考:内存配置提供程序


38
投票

我不知道 NSubstitute,但这就是我们在 Moq 中可以做到的。 在这两种情况下,方法都是相同的。

GetValue<T>()
内部使用
GetSection()

您可以模拟

GetSection
并返回您自己的
IConfigurationSection

这包括两个步骤。

1)。为

IConfigurationSection
(mockSection) 创建模拟并设置
.Value
属性以返回所需的配置值。

2)。在Mock

上Mock 
.GetSection< IConfiguration >,并返回上面的
mockSection.Object

Mock<IConfigurationSection> mockSection = new Mock<IConfigurationSection>();
mockSection.Setup(x=>x.Value).Returns("ConfigValue");

Mock<IConfiguration> mockConfig = new Mock<IConfiguration>();
mockConfig.Setup(x=>x.GetSection("ConfigKey")).Returns(mockSection.Object);

21
投票

模拟I配置

Mock<IConfiguration> config = new Mock<IConfiguration>();

设置获取

config.SetupGet(x => x[It.Is<string>(s => s == "DeviceTelemetryContainer")]).Returns("testConatiner");
config.SetupGet(x => x[It.Is<string>(s => s == "ValidPowerStatus")]).Returns("On");

5
投票

IConfiguration.GetSection<T>
必须被间接嘲笑。我不完全理解为什么,因为 NSubstitute,如果我理解正确的话,会创建它自己的您正在动态模拟的接口的实现(在内存汇编中)。但这似乎是唯一可以做到的方法。包括顶级部分和常规部分。

var config = Substitute.For<IConfiguration>();
var configSection = Substitute.For<IConfigurationSection>();
var configSubSection = Substitute.For<IConfigurationSection>();
configSubSection.Key.Returns("SubsectionKey");
configSubSection.Value.Returns("SubsectionValue");
configSection.GetSection(Arg.Is("SubsectionKey")).Returns(configSubSection);
config.GetSection(Arg.Is("TopLevelSectionName")).Returns(configSection);

var topLevelSection = Substitute.For<IConfigurationSection>();
topLevelSection.Value.Returns("TopLevelValue");
topLevelSection.Key.Returns("TopLevelKey");
config.GetSection(Arg.Is<string>(key => key != "TopLevelSectionName")).Returns(topLevelSection);

// GetValue mocked indirectly.
config.GetValue<string>("TopLevelKey").Should().Be("TopLevelValue");
config.GetSection("TopLevelSectionName").GetSection("SubsectionKey").Value.Should().Be("SubsectionValue");

3
投票

我可以想象在一些罕见的情况下它是需要的,但以我的拙见,大多数时候,嘲笑 IConfiguration 会突出显示代码设计缺陷。

您应该尽可能依赖选项模式来提供对部分配置的强类型访问。此外,如果您的应用程序配置错误,它还会简化测试并使您的代码在启动期间失败(而不是在运行时执行读取 IConfiguration 的代码时失败)。

如果你真的(真的)需要模拟它,那么我建议不要模拟它,而是使用内存中的配置来模拟它,如 @Nkosi 的 answer

中所述

1
投票

虽然 Nkosi 的答案非常适合简单的结构,但有时您希望能够拥有更复杂的对象(如数组),而无需重复整个部分路径,并且能够使用预期的类型本身。如果您不太关心性能(并且您应该进行单元测试吗?),那么此扩展方法可能会有所帮助。

public static void AddObject(this IConfigurationBuilder cb, object model) {
    cb.AddJsonStream(new MemoryStream(Encoding.UTF8.GetString(JsonConvert.SerializeObject(model))));
}

然后像这样使用它

IConfiguration configuration = new ConfigurationBuilder()
    .AddObject(new {
       SectionName = myObject
    })
    .Build();

0
投票

使用SetupGet方法来模拟配置值并返回任何字符串。

var configuration = new Mock<IConfiguration>();
configuration.SetupGet(x => x[It.IsAny<string>()]).Returns("the string you want to return");

0
投票

我发现这个解决方案可以可靠地用于我的 XUnit C# 测试和相应的 C# 代码:

在控制器中

string authConnection = this._config["Keys:AuthApi"] + "/somePathHere/Tokens/jwt";

在 XUnit 测试中

Mock<IConfiguration> mockConfig = new Mock<IConfiguration>();
mockConfig.SetupGet(x => x[It.Is<string>(s => s == "Keys:AuthApi")]).Returns("some path here");

0
投票

对于简单的顶级键和值,我使用了这段代码,它对我有用:

var _configuration = Substitute.For<IConfiguration>();
_configuration["TopLevelKey"].Returns("TopLevelValue");

0
投票

我们需要模拟 IConfiguration.GetSection ,它在 GetValue 扩展方法中执行。

您注入 IConfiguration:

private readonly Mock<IConfiguration> _configuration;

以及 //Arrange 部分中的:

_configuration.Setup(c => c.GetSection(It.IsAny<String>())).Returns(new Mock<IConfigurationSection>().Object);

它对我来说就像一种魅力。

© www.soinside.com 2019 - 2024. All rights reserved.