How to update IOptions / configuration in ASP.NET Core integration tests?

685 views Asked by At

I have an ASP.NET Core web application and writing integration test to run the server in-memory using WebApplicationFactory (ie. https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests)

As usual, application services are configurable, in other words we inject using IOptions<> into various services. I'd like to test different configuration scenarios, which I'd define dynamically during test runtime.

For example:

public class EmailSenderOptions
{
    public string Sender { get; set; }
}

// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<EmailSenderOptions>(config.GetSection("EmailSender"));

// Test
[TestFixture]
public class EmailSenderTests
{
     WebApplicationFactory<MyStartup> SUT = //omitted...

     [TestCase("[email protected]")]
     [TestCase("[email protected]")]
     public void TestSender(string sender)
     {
         var client = SUT.CreateClient();
         SUT.Configuration.Set("EmailSender:Sender", sender); // <-- how?
         
         await client.GetAsync("/email");
     }
}

I'm aware that I could create test implementation for IOptions, but that would be much more difficult especially if IOptionsMonitor is being used. So I'm looking for a way just to overwrite the configuration values runtime

2

There are 2 answers

0
balazska On BEST ANSWER

We can obtain IConfiguration from services, since it has been registered as a singleton by host builder during application startup. We can also set values using the indexer. The "trick" is we need also to call reload (it is available through IConfigurationRoot interface) to populate changes

internal static void SetConfiguration(this WebApplicationFactory<TStartup> Sut, string key, string value)
{
    var config = Sut.Services.GetRequiredService<IConfiguration>();
    if (config is IConfigurationRoot root)
    {
        root[key] = value;
        root.Reload();
    }
}

// Call like
SUT.SetConfiguration("EmailSender:Sender", "[email protected]");

Other alternative would be to create an own IConfigurationSource and supply values through a dictionary. This is much more coda and also requires to implement IConfigurationProvider and still need to call reload.

0
Volodymyr Dombrovskyi On

In case you would like to use your typed EmailSenderOptions to set value in tests, you may consider MutableOptions NuGet Package (I am the author of that package). It would look like that:

// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigureMutable<EmailSenderOptions>(config.GetSection("EmailSender"));

// Test
// Instead of SUT.Configuration.Set("EmailSender:Sender", sender);
serviceProvider.GetRequiredService<IOptionsMutator<EmailSenderOptions>>().Mutate(options => options with { Sender = sender });