简体   繁体   中英

ASP.NET Core UseSetting from integration test

I have an integration tests project that uses .UseSetting() in the test class, as follows:

public AccessTokenRetrieval() : base(nameof(AccessTokenRetrieval))
{
    var connectionString = GetConnectionString();
    var dbSettings = new DbSettings(connectionString);
    _userGroupRepository = new UserGroupRepository(dbSettings);
    _roleRepository = new RoleRepository(dbSettings);
    _userRepository = new UserRepository(dbSettings);

    _server = new TestServer(new WebHostBuilder()
        .UseStartup<Startup>()
        .UseEnvironment("IntegrationTest")
        .UseSetting("IntegrationTestConnString", dbSettings.IdentityServerConnectionString));
    _handler = _server.CreateHandler();
    _client = _server.CreateClient();
}

I would now like to retrieve that setting in the Startup.cs of my actual project. I attempted to do so using:

public void ConfigureIntegrationTestServices(IServiceCollection services)
{
    var connectionString = Configuration.GetValue<string>("IntegrationTestConnString");

    BuildIdentityServerTests(services, connectionString);
    AddCoreServices(services, connectionString);
}

but that seems to return null.

What is the proper way to retrieve this setting?

Per-test setting

To pass a setting into a particular TestHost instance, you could use ConfigureServices call and override default setting registration.

First of all, register your default DbSettings instance in Startup.ConfigureServices method. It's mandatory to use TryAll call:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.TryAddSingleton(new DbSettings(...));
    }
}

Register a mocked instance in WebHostBuilder.ConfigureServices call:

_server = new TestServer(new WebHostBuilder()
    .UseStartup<Startup>()
    .ConfigureServices(services =>
        {
            services.AddSingleton(new DbSettings(...));
        }
    );

When you try to resolve a DbSettings in the application code, you will get a mocked instance. Because WebHostBuilder.ConfigureServices is executed first and TryAdd call prevents to register a default instance.

This hint allows to replace any DI dependency you registered.

Global setting

To set an invariant (accross all tests) setting, set an process-scoped environment variable instead of UseSetting call:

Environment.SetEnvironmentVariable("foo", "bar");

var _server = new TestServer(new WebHostBuilder()
    .UseStartup<Startup>()
);

Then read it:

public void ConfigureServices(IServiceCollection services)
{
    string setting1 = Configuration["foo"];
    string setting2 = Environment.GetEnvironmentVariable("foo");
}

You need to add environment variable provider to read variables from Configuration :

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)

        //this config provider is mandatory to read env vars from Configuration
        .AddEnvironmentVariables();

    Configuration = builder.Build();
}

You could just inject the IConfigurationRoot where you could just use a local config file in the test project. You'd lose the default environment-specific config overrides that are the default in the new project template, but I personally don't use those for configuration management anyway (preferring non-committed appsetting.local.json instead which can be unique to developers and to the CI server).

var configuration = new ConfigurationBuilder()
    .SetBasePath(AppContext.BaseDirectory)
    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
    .AddJsonFile("appsettings.local.json", optional: true, reloadOnChange: true)
    .AddInMemoryCollection(new Dictionary<string, string>
    {
        {"myConfig:setting1", "value1"},
        {"myConfig:setting2", "value2"}
    })
    .Build();

var server = new TestServer(new WebHostBuilder()
    .UseUrls("http://localhost:5001")
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseStartup<Startup>()
    .ConfigureServices(services => services.AddSingleton(configuration)));

And for Startup:

public class Startup
{
    public Startup(IConfigurationRoot configuration)
    {
        this.Configuration = configuration;
    }

    ...
}

Edit: While this does work, it also seems to break IOptionsSnapshot configuration injections from recognizing config file changes at run-time. I'm going to leave this answer, but will also keep digging for better way without injecting a custom service just to support test/fixture-specific config overrides.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM