In this article, we’ll unveil a straightforward approach to mock IOptions<T>, ensuring our tests remain both thorough and independent of real-world configurations.

To download the source code for this article, you can visit our GitHub repository.

Let’s dive in.

Understanding IOptions<T>

ASP.NET Core’s configuration system has been praised for its flexibility and extensibility. Central to this is the IOptions<T> interface.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

This pattern is incredibly valuable as it provides us with a type-safe way of accessing configuration settings, ensuring we don’t mistype string keys or access non-existent values.

At its core, IOptions<T> is a service that retrieves configuration values bound to a particular class.

For instance, if we have an app settings section for database configuration, we can directly map that section to a DatabaseSettings class and then inject IOptions<DatabaseSettings> interface wherever we need those settings.

However, while this makes accessing configurations seamless in production, it can pose challenges during testing.

As we move forward, we’ll see how to simulate this interface effectively, allowing our unit tests to remain environment-agnostic.

Setting Up Our Environment

Before diving into the mock, ensuring our testing environment is primed and ready is essential.

Installing NuGet Packages

To get started, we will need a couple of NuGet packages:

dotnet add package Microsoft.Extensions.Options
dotnet add package Moq

First, we install Microsoft.Extensions.Options package that provides the necessary interfaces and types for options and configurations. Then, we install the Moq package – a popular mocking library for .NET.

Creating DatabaseConfiguration Class

Now, let’s create a new class that will represent our configuration:

public class DatabaseConfiguration
{
    public string ConnectionString { get; set; }
}

The DatabaseConfiguration instance will provide a ConnectionString property that we will use to retrieve the database connection string.

After that, we register it in our Program.cs file:

builder.Services.Configure<DatabaseConfiguration>(options =>
        builder.Configuration.GetSection("DatabaseConfiguration").Bind(options));

Thus, we let our application know that it should find the DatabaseConfiguration section in our appsettings.json file, map it to DatabaseConfiguration class, and register it as an available configuration.

Mocking IOptions<T>

Now, with our environment set, let’s see our available options to emulate IOptions<T>.

We will start by creating a new instance of our DatabaseConfiguration class in our Setup method:

_configData = new DatabaseConfiguration { ConnectionString = "TestConnectionString" };

Mocking IOptions<T> Using Moq

First, we can use a mocking library like Moq if we prefer a more granular approach:

var configMock = new Mock<IOptions<DatabaseConfiguration>>();
configMock.Setup(x => x.Value).Returns(_configData);

Here, we tell Moq that whenever the Value property of our IOptions<DatabaseConfiguration> is accessed, it should return our configData object.

Mocking IOptions<T> Using Microsoft.Extensions.Options

Alternatively, ASP.NET Core provides us with a straightforward way to wrap our configuration data.

In order to do so, we can use the OptionsWrapper class:

var configWrapper = new OptionsWrapper<DatabaseConfiguration>(_configData);

Or we can use the Options helper class:

var configWrapperUsingHelper = Options.Create(_configData);

Both methods work in a similar fashion and return a new instance of  IOptions<DatabaseConfiguration. It is possible to use them alternatively because under the hood Options.Create(configData) creates a new instance of OptionsWrapper<T> and resolves T type on its own.

Implementing the Mock in Tests

Now that we’ve set up our mocks, it’s time to integrate them into our tests and see them in action.

Incorporating mocked IOptions<T> ensures that our tests are purely evaluating our code’s functionality, detached from any external configurations.

Let’s start with creating a service that we would like to test:

public class CustomerService
{
    private readonly DatabaseConfiguration _configuration;

    public CustomerService(IOptions<DatabaseConfiguration> configuration)
    {
        _configuration = configuration.Value;
    }

    public string GetConnectionString() => _configuration.ConnectionString;
}

For the sake of our example, we will keep CustomerService simple.

It consists of a constructor that accepts an IOptions<DatabaseConfiguration> instance and populates a DatabaseConfiguration property.

Consequently, our only method – GetConnectionString(), simply returns the connection string retrieved from the configuration.

Now, it’s time to write some tests for our new class.

Testing Using Moq

First, we will test our class using Moq:

[Test]
public void WhenMockingIOptionsWithMoq_ThenCorrectConnectionStringIsReturned() 
{ 
    var configMock = new Mock<IOptions<DatabaseConfiguration>>();
    configMock.Setup(x => x.Value).Returns(_configData);

    var sut = new CustomerService(configMock.Object);
    var result = sut.GetConnectionString();
    
    Assert.That(result, Is.EqualTo("TestConnectionString"));
}

In the beginning, we initialize our mock as we have shown above. Then we initialize our CustomerService instance by passing the mocked instance of IOptions<DatabaseConfiguration> and call GetConnectionString() method.

Lastly, we assert that the returned value is the same as the expected response.

Testing Using OptionsWrapper<T>

Now, let’s create a similar test, but instead of using Moq to create our mock, we will use the OptionsWrapper<T> class:

[Test]
public void WhenMockingIOptionsWithOptionsWrapper_ThenCorrectConnectionStringIsReturned() 
{ 
    var configWrapper = new OptionsWrapper<DatabaseConfiguration>(_configData);

    var sut = new CustomerService(configWrapper);
    var result = sut.GetConnectionString();
    
    Assert.That(result, Is.EqualTo("TestConnectionString"));
}

Testing Using Options Helper Class

Lastly, let’s try using the Options helper class:

[Test]
public void WhenMockingIOptionsWithOptionsHelper_ThenCorrectConnectionStringIsReturned() 
{ 
    var configWrapperUsingHelper = Options.Create(_configData);

    var sut = new CustomerService(configWrapperUsingHelper);
    var result = sut.GetConnectionString();
    
    Assert.That(result, Is.EqualTo("TestConnectionString"));
}

Ultimately, every test succeeds, and our GetConnectionString() method returns the expected value:

mocking-ioptions-tests-results

Reviewing the test outcomes, it becomes evident that leveraging the native mocking techniques of ASP.NET Core is generally more efficient than resorting to an external mocking library like Moq. However, for more intricate mocking needs or when verifying complex behaviors, the advanced capabilities of Moq might offer advantages that go beyond mere efficiency.

Conclusion

In this guide, we navigated the nuances of mocking IOptions<T> in ASP.NET Core, emphasizing the importance of isolating tests from external configurations. With these techniques, our unit tests become more predictable, ensuring our applications remain robust and reliable.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!