How do I mock IConfiguration with Moq?
Asked Answered
C

2

8

How do I mock code like this in my unit test. I'm using xUnit and Moq in asp.net core 5. I'm new to xUnit and Moq.

var url = configuration.GetSection("AppSettings").GetSection("SmsApi").Value;

The configuration object is already injected into the constructor.

This is what I currently have in my unit test class

public class UtilityTests
{
    private readonly Utility sut;

    public UtilityTests()
    {
        var mockConfig = new Mock<IConfiguration>();
        var mockConfigSection = new Mock<IConfigurationSection>();
        //mockConfigSection.Setup(x => x.Path).Returns("AppSettings");
        mockConfigSection.Setup(x => x.Key).Returns("SmsApi");
        mockConfigSection.Setup(x => x.Value).Returns("http://example.com");
        
        mockConfig.Setup(x => x.GetSection("AppSettings")).Returns(mockConfigSection.Object);
        
        sut = new Utility(mockConfig.Object);
    }

    [Fact]
    public void SendSmsShdReturnTrue()
    {
        var fixture = new Fixture();
        
        var result = sut.SendSms(fixture.Create<string>(), fixture.Create<string>());
        result.Should().BeTrue();
    }
}
Cormier answered 1/5, 2021 at 17:55 Comment(3)
Is it appSettings.json or web.config ? Sample file would be helpful.Scrim
It's .net core so app settings.jsonCormier
@Cormier Did any of the suggested solution work for you?Vergievergil
P
3

Alternative approach tp introduce a class to represent section of the configuration, then use IOptions interface to inject it to the constructor.

Your tests then become simple to configure without mocking, just create an instance and pass it to the constructor.

Something like below:

class SmsApiSettings
{
    public string Url { get; set; }
}

Register during startup

services.Configure<SmsApiSettings>(Configuration.GetSection("SmsApi"));

Constructor

public class ClassUnderTest
{
    private readonly SmsApiSettings _smsApiSettings;

    public ClassUnderTest(IOptions<> smsOptions)
    {
        _smsApiSettings = smsOptions.Value;
    }
}

Tests

var settings = new SmsApiSettings { Url = "http://dummy.com" };
var options = Options.Create(settings);

var sut = new ClassUnderTest(options);

Enjoy happy life without mocks ;)

Producer answered 1/5, 2021 at 23:22 Comment(3)
I like this method, its a more elegant way to handle it. Thanks @ProducerCormier
I feel like a lot more context would not go amiss. (Around the code snippets.)Transportation
Yeah this looks nice but what is Options.Create() ? What namespace? Doesn't compile for me so downvoted.Gebhardt
V
15

The truth is that the IConfiguration should not be mocked. Instead it should be built.

via dictionary

Data

var configForSmsApi = new Dictionary<string, string>
{
    {"AppSettings:SmsApi", "http://example.com"},
};

Usage

var configuration = new ConfigurationBuilder()
    .AddInMemoryCollection(configForSmsApi)
    .Build();

via json file

Data

{
  "AppSettings": {
    "SmsApi": "http://example.com"
  }
}

Usage

var configuration = new ConfigurationBuilder()
    .AddJsonFile("smsapi.json", optional: false)
    .Build();
Vergievergil answered 4/5, 2021 at 7:26 Comment(3)
Why should IConfiguration not be mocked?Diadiabase
@Yíu It can be mocked, but it is easier to build one via a Dictionary. Otherwise you have to mock several GetSection with different parameters (see the OP's example).Vergievergil
@Yíu You might also use extension methods of IConfiguration. You cannot mock those.Allred
P
3

Alternative approach tp introduce a class to represent section of the configuration, then use IOptions interface to inject it to the constructor.

Your tests then become simple to configure without mocking, just create an instance and pass it to the constructor.

Something like below:

class SmsApiSettings
{
    public string Url { get; set; }
}

Register during startup

services.Configure<SmsApiSettings>(Configuration.GetSection("SmsApi"));

Constructor

public class ClassUnderTest
{
    private readonly SmsApiSettings _smsApiSettings;

    public ClassUnderTest(IOptions<> smsOptions)
    {
        _smsApiSettings = smsOptions.Value;
    }
}

Tests

var settings = new SmsApiSettings { Url = "http://dummy.com" };
var options = Options.Create(settings);

var sut = new ClassUnderTest(options);

Enjoy happy life without mocks ;)

Producer answered 1/5, 2021 at 23:22 Comment(3)
I like this method, its a more elegant way to handle it. Thanks @ProducerCormier
I feel like a lot more context would not go amiss. (Around the code snippets.)Transportation
Yeah this looks nice but what is Options.Create() ? What namespace? Doesn't compile for me so downvoted.Gebhardt

© 2022 - 2024 — McMap. All rights reserved.