Engineer
Engineer

Reputation: 559

C# how to mock Configuration.GetSection("foo:bar").Get<List<string>>()

I have a list like following in config.json file `

{
  "foo": {
    "bar": [
      "1",
      "2",
      "3"
    ]
  }
}`

I am able to get the list at run-time using

Configuration.GetSection("foo:bar").Get<List<string>>()

I want to mock the configuration.GetSection to write unit test.

Following syntax is failing

mockConfigRepo
    .SetupGet(x => x.GetSection("reportLanguageSettings:reportLanguageList").Get<List<string>>())
    .Returns(reportLanguages);

Upvotes: 46

Views: 53331

Answers (8)

Bryant
Bryant

Reputation: 346

Can also use AddInMemoryCollection and pass it a dictionary like so:

var inMemorySettings = new Dictionary<string, string>
{
    { "foo:bar", "abc" },
    { "foo:baz", "definitely" },
};
IConfiguration configuration = new ConfigurationBuilder()
    .AddInMemoryCollection(inMemorySettings)
    .Build();

Upvotes: 2

Ganesh
Ganesh

Reputation: 11

You need to add the Microsoft.Extensions.Configuration.Json NuGet package to have access to the method AddJsonStream():

dotnet add package Microsoft.Extensions.Configuration.Json

Upvotes: 0

Wilson Gotti Neto
Wilson Gotti Neto

Reputation: 39

For those using FakeItEasy, you can simply do:

var fakeConfiguration = A.Fake<IConfiguration>();
var fakeConfigurationSection = A.Fake<IConfigurationSection>();

fakeConfigurationSection["bar"] = "Your desired value here";

A.CallTo(() => fakeConfiguration.GetSection("foo"))
                .Returns(fakeConfigurationSection);

This might be helpful for getting ConnectionStrings, just replace foo by ConnectionStrings and bar by your connection string key

Upvotes: 0

Kristaps Vītoliņš
Kristaps Vītoliņš

Reputation: 131

Just to add on Ahamed Ishak answer. Convert actual objects to JSON would clean up the code and types are respected. Avoid string typo errors, etc.

        var appSettings = JsonConvert.SerializeObject(new
        {
            Security = new SecurityOptions {Salt = "test"}
        });

        var builder = new ConfigurationBuilder();

        builder.AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(appSettings)));

        var configuration = builder.Build();

Upvotes: 9

Ahamed Ishak
Ahamed Ishak

Reputation: 1062

I was able to solve it using ConfigurationBuilder. Hope this will help

  var appSettings = @"{""AppSettings"":{
            ""Key1"" : ""Value1"",
            ""Key2"" : ""Value2"",
            ""Key3"" : ""Value3""
            }}";

  var builder = new ConfigurationBuilder();

  builder.AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(appSettings)));

  var configuration= builder.Build();

Upvotes: 47

Alessio Di Salvo
Alessio Di Salvo

Reputation: 281

In general, if you have a key/value at the root level and you want to mock this piece of code:

var threshold = _configuration.GetSection("RootLevelValue").Value;

you can do:

var mockIConfigurationSection = new Mock<IConfigurationSection>();
mockIConfigurationSection.Setup(x => x.Key).Returns("RootLevelValue");
mockIConfigurationSection.Setup(x => x.Value).Returns("0.15");
_mockIConfiguration.Setup(x => x.GetSection("RootLevelValue")).Returns(mockIConfigurationSection.Object);

If the key/value is not at the root level, and you want to mock a code that is like this one:

var threshold = _configuration.GetSection("RootLevelValue:SecondLevel").Value;

you have to mock Path as well:

var mockIConfigurationSection = new Mock<IConfigurationSection>();
mockIConfigurationSection.Setup(x => x.Path).Returns("RootLevelValue");
mockIConfigurationSection.Setup(x => x.Key).Returns("SecondLevel");
mockIConfigurationSection.Setup(x => x.Value).Returns("0.15");

and so on for the third level:

var threshold = _configuration.GetSection("RootLevelValue:SecondLevel:ThirdLevel").Value;

you have to mock Path as well:

var mockIConfigurationSection = new Mock<IConfigurationSection>();
mockIConfigurationSection.Setup(x => x.Path).Returns("RootLevelValue:SecondLevel");
mockIConfigurationSection.Setup(x => x.Key).Returns("ThirdLevel");
mockIConfigurationSection.Setup(x => x.Value).Returns("0.15");

Upvotes: 19

Pasha Banks
Pasha Banks

Reputation: 491

I've encountered same issue and found that I needed to create a mock IConfigurationSection for every element in the array, as well as the array node itself, and then setup the parent node to return children, and children to return their values. In OP example, it would look like this:

        var oneSectionMock = new Mock<IConfigurationSection>();
        oneSectionMock.Setup(s => s.Value).Returns("1");
        var twoSectionMock = new Mock<IConfigurationSection>();
        twoSectionMock.Setup(s => s.Value).Returns("2");
        var fooBarSectionMock = new Mock<IConfigurationSection>();
        fooBarSectionMock.Setup(s => s.GetChildren()).Returns(new List<IConfigurationSection> { oneSectionMock.Object, twoSectionMock.Object });
        _configurationMock.Setup(c => c.GetSection("foo:bar")).Returns(fooBarSectionMock.Object);

P.S. I'm using Moq, so please translate to your mock library of choice.

P.P.S. If you are interested in why this ends up working, what unmockable Get() method does, or have a more complex scenario than OP, reading this class may be helpful: https://github.com/aspnet/Extensions/blob/release/2.1/src/Configuration/Config.Binder/src/ConfigurationBinder.cs

Upvotes: 39

Nuh Metin G&#252;ler
Nuh Metin G&#252;ler

Reputation: 1083

You can try alternative ways. For example, you can try to create an instance of ConfigurationBuilder in your test class:

string projectPath = AppDomain.CurrentDomain.BaseDirectory.Split(new String[] { @"bin\" }, StringSplitOptions.None)[0];
IConfiguration config = new ConfigurationBuilder()
   .SetBasePath(projectPath)
   .AddJsonFile("config.json")
   .Build();

Note: Please don't forget to add your config.json file to your test project too.

Upvotes: 12

Related Questions