Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Append test project appSettings to ASP.NET Core integration tests

I'm creating ASP.NET Core integration tests (xUnit based) following these docs. I want to start the test web server with its own appsettings.json. My abbreviated folder structure is:

\SampleAspNetWithEfCore
\SampleAspNetWithEfCore\SampleAspNetWithEfCore.csproj
\SampleAspNetWithEfCore\Startup.cs
\SampleAspNetWithEfCore\appsettings.json
\SampleAspNetWithEfCore\Controllers\*

\SampleAspNetWithEfCore.Tests\SampleAspNetWithEfCore.Tests.csproj
\SampleAspNetWithEfCore.Tests\IntegrationTests.cs
\SampleAspNetWithEfCore.Tests\appsettings.json

then I have these utilities:

public static class ServicesExtensions
{
    public static T AddOptions<T>(this IServiceCollection services, IConfigurationSection section)
        where T : class, new()
    {
        services.Configure<T>(section);
        services.AddSingleton(provider => provider.GetRequiredService<IOptions<T>>().Value);

        return section.Get<T>();
    }
}

and inside Startup.cs ConfigureServices(...) I do this:

services.AddOptions<SystemOptions>(Configuration.GetSection("System"));

Referring to the appsettings.json section like this:

"System": {
  "PingMessageSuffix": " suffix-from-actual-project"
}

So far so good: this is picked up in a strongly typed manner. My controller gets a SystemOptions instance that mirrors the json structure, and the controller uses the suffix correctly.

The problems are with building the Integration Tests WebHost. I want to run the Startup from my real project as is, with its own appsettings.json settings, but as an extra layer of settings I want the appsettings.json from my test csproj to be added, overriding any settings if applicable. This is my appsettings from the test project:

"System": {
  "PingMessageSuffix": " suffix-from-test-appsettings"
}

Here's what I've tried:

public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder
            .UseStartup<Startup>()
            .ConfigureAppConfiguration(config => config
                .AddJsonFile("appsettings.json")
            );
    }
}

However, this doesn't work. If I hit a breakpoint in my controller I see only the settings from the base project. The controller just echo's the config value currently, and logically the return result is also not as expected.

The documentation doesn't mention "appsettings" anywhere on the page.

Bottom line: How can you add a layer of appSettings from a test project's appsettings.json file when running ASP.NET Core integration tests?

like image 237
Jeroen Avatar asked Dec 10 '18 11:12

Jeroen


2 Answers

Solved it like this:

  1. For appsettings.json in the Test project set the Properties:
    • Build Action to Content
    • Copy to Output Directory to Copy if newer
  2. Use a custom WebApplicationFactory like so:

    public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            var configuration = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .Build();
    
            // Note:         ↓↓↓↓
            builder.ConfigureTestServices(services => 
                services.AddOptions<SystemOptions>(configuration.GetSection("System"))
            );
        }
    }
    

And voila: it works!

The first step is needed to make the ConfigurationBuilder find your json file easily. The second step subtly uses a ...TestServices configuration (if you use the regular ConfigureServices method it'll be called before the Startup's service configuration and get overwritten).

Footnote: commenters (on the question) have mentioned it might be better to have a appsettings.ci.json file in the SUT project, and control things by environment (which you'd set via launch settings or via the WebHostBuilder). The documentation links to a few closed GitHub issues that suggest the same thing: 8712, 9060, 7153. Depending on your scenario and taste, that might be a better or more idiomatic solution.

like image 67
Jeroen Avatar answered Nov 14 '22 00:11

Jeroen


Update Feb 2020 - ASP.NET Core 3.0 and above

The way you do this has changed, you need to use the ConfigureAppConfiguration delegate.

public class HomeControllerTests : IClassFixture<WebApplicationFactory<Startup>>
{
    private readonly WebApplicationFactory<Startup> _factory;

    public HomeControllerTests(WebApplicationFactory<Startup> factory)
    {
        var projectDir = Directory.GetCurrentDirectory();
        var configPath = Path.Combine(projectDir, "appsettings.json");

         //New              ↓↓↓              
        _factory = factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureAppConfiguration((context,conf) =>
            {
                conf.AddJsonFile(configPath);
            });

        });
     }
}

Credit to: https://gunnarpeipman.com/aspnet-core-integration-tests-appsettings/

like image 39
DalSoft Avatar answered Nov 13 '22 23:11

DalSoft