asp.net-coreintegration-testingasp.net-core-configuration

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


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("noreply@mycompany.com")]
     [TestCase("easy-hookup@hackersite.com")]
     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


Solution

  • 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", "sender@mail.com");
    

    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.