3

When running integration tests, I would like to use a partially randomly generated connection string for the DbContext where the database part of the connection string has a format like AppName_{RandomGuid}. This would allow multiple integration test instances to run at the same time against a real database without anyone stepping on anyone else, as the integration test databases would be created, and destroyed as needed on the central dev database server. The entire connection string would look like Server=tcp:0.0.0.0,49172;Database=AppName_{RandomGuid};User Id=;Password=;TrustServerCertificate=True.

What I have tried is something like this with the WebApplicationFactory:

private static readonly WebApplicationFactory<Program> WebApplication = new WebApplicationFactory<Program>()
    .WithWebHostBuilder(builder =>
    {
        var testConfigurationSettings = new Dictionary<string, string>
        {
            { $"Database:ConnectionString", $"Server=tcp:0.0.0.0,49172;Database=AppName_{Guid.NewGuid()};User Id=;Password=;TrustServerCertificate=True" }
        };

        var testConfiguration = new ConfigurationBuilder()
            .AddInMemoryCollection(testConfigurationSettings)
            .Build();

        builder.UseConfiguration(testConfiguration);
    });

This would work expect that in my Program.cs I have this line:

var builder = WebApplication.CreateBuilder(args);

// THIS LINE
builder.Configuration.AddJsonFile(path: $"appsettings.Development.{Environment.MachineName}.json", optional: true);

builder.Services.AddApplicationServices(builder.Configuration);

var app = builder.Build();

await app.RunAsync();

This allows developers (based on their machine name) to override their own settings as they want when running the application locally; importantly, each dev will have their own specific config file that, at a minimum, has a connection string which points to their own dev specific database. My problem with this solution is the line which adds the developer specific config is overriding the config registered in the WithWebHostBuilder for the integration test, because, the Program.cs executes after the WithWebHostBuilder code.

The first part is my actual problem, and the second is my current attempted solution which is so close, and clean to doing exactly what I want.

Mark Lisoway
  • 619
  • 1
  • 6
  • 20
  • With something like https://learn.microsoft.com/ro-RO/ef/core/miscellaneous/multitenancy#multiple-databases-and-connection-strings && https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlconnectionstringbuilder?view=dotnet-plat-ext-6.0? Or did you want to just pick one connection string at startup? – Jeremy Lakeman Jun 16 '22 at 01:37
  • @JeremyLakeman I don't need multiple connection strings at once. And I don't think the builder will really help here; I am already pulling the connection strings in their entirety from the appsettings (or generated as needed). My issue is that I wanted to partially generate a new connection string for the integration test database each time the tests are run. But my developer settings files were overriding that, because, the developer settings were added during the Program.cs startup which was after the integration connection string was added. – Mark Lisoway Jun 16 '22 at 01:54
  • Ah right. For unit tests, rather than trying to tweak config, I would explicitly reconfigure your db context services; `builder.ConfigureServices(s => s.AddDbContext<..>( etc ))`. – Jeremy Lakeman Jun 16 '22 at 02:22

1 Answers1

0

This is the solution I am using for now that works, but might be considered a bit hacky. If no other solutions come soon, then I'll mark this as the accepted answer.

Basically, I added a field to my appsettings called IsTesting, and by default it is false, but is set to true when running the integration tests:

private static readonly WebApplicationFactory<Program> WebApplication = new WebApplicationFactory<Program>()
    .WithWebHostBuilder(builder =>
    {
        var testConfigurationSettings = new Dictionary<string, string>
        {
            { "IsTesting", "true" },
            { $"Database:ConnectionString", $"Server=tcp:0.0.0.0,49172;Database=AppName_{Guid.NewGuid()};User Id=;Password=;TrustServerCertificate=True" }
        };

        var testConfiguration = new ConfigurationBuilder()
            .AddInMemoryCollection(testConfigurationSettings)
            .Build();

        builder.UseConfiguration(testConfiguration);
    });

This way I can check for the setting in my Program.cs and skip applying the developer configuration files if needed that were previously overriding the integration test settings:

var config  = configurationManager.Get<ApplicationConfiguration>();

if (config.IsTesting)
{
    return;
}

configurationManager.AddJsonFile(path: $"appsettings.Development.{Environment.MachineName}.json", optional: true);
Mark Lisoway
  • 619
  • 1
  • 6
  • 20