Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Accessing Configuration in .NET Core Test Projects


:P
On this page:

If you've been following my blog you know I've written a bit about how the configuration system works in .NET Core and specifically in ASP.NET Core using the dependency injection system:

Both posts describe how you can set up configuration using the various now automatically configured Configuration services in the ASP.NET Startup class and its ConfigureServices() method.

But how do you do this in a Test or any other ASP.NET Core project where configuration isn't automatically configured?

Why Configuration? My simple Use Case - Sensitive Values

When running test projects it's often possible to get away without having to configure a configuration class and just provide explicit values. But often you actually need full dependency injection to be hooked up in order to get Configuration injected into dependencies which brings up two issues:

  • How to get access to the Configuration Provider
  • Hooking up Dependency Injection so Configuration can be injected

My first use case is simple and doesn't require dependency injection: I simply need configuration to handle reading some configuration information in order to test sending an email to check out a new mail provider. I explicitly need to make it so I don't hardcode the sensitive email values and they don't end up in my Git repo. So it would be nice to use UserSecrets as well as get the values from the already existing configuration object config in appSettings.json - same as the Web application that actually runs this code.

The second scenario involves using a business object that also uses this email sending logic in an integration test. Here the configuration object is injected into the business object so I need to have dependency injection available.

Let's take a look at both of these scenarios.

IConfiguration in non-ASP.NET Projects

ASP.NET Core 2.0 now automatically provides an IConfiguration provider that handles input from appsettings.json (including the .Development file), UserSecrets and Environment variable which is great. Configuration is such a core thing that almost every application needs it and with ASP.NET Core 2.0 you don't have to worry about setting up the configuration system manually.

However in a test project that onus falls on you. Unfortunately it's not quite so easy to do this as in ASP.NET because test projects don't automatically configure either a Dependency injection container with common objects, or a configuration provider, so this has to be handled manually.

Fortunately the process to do this is pretty straight forward.

Setting up and Retrieving a Raw Configuration Object

In my test projects I generally add a TestHelper class that provides a few commonly used values, but I also add a few helper methods and one of the methods I typically create is a GetApplicationConfiguration() class. In this application I have a configuration class call KavaDocsConfiguration which is a nested class that contains a bunch of values along with a nested Email object that contains the configuration values I need for my mail test code.

Here's what my configuration in appsettings.json looks like:

// appsettings.json
{
  "Logging": {...},
  "KavaDocs": {
    "ApplicationName": "KavaDocs",
    "ConnectionString": null,    
    "ApplicationBasePath": "/",
    "ApplicationHomeUrl": "https://localhost:5000",
    "Email": {
      "MailServer": null,
      "MailServerUsername": null,
      "MailServerPassword": null,
      "SenderName": "Kava Docs Administration",
      "SenderEmail": "support@kavadocs.com",
      "AdminSenderEmail": "support@kavadocs.com",
      "UseSsl": true
    }
  }
}

The UserSecrets JSON data then overrides the sensitive values that are stored outside of the project root so they don't get checked into Git. The file is secrets.json in the local user's User Secrets location:

{
  "KavaDocs": {
    "ConnectionString": "server=.;database=kavadocs;integrated security=true;",
    "Email": {
      "MailServer": "smtp.mailserver.org",
      "MailServerUsername": "seekrity@darko.com",
      "MailServerPassword": "123456c37a623c686f04ab654321",
      "UseSsl": true
    }
  } 
}

To access the configuration I have to build an IConfigurationRoot explicitly, which is the part that ASP.NET handles explicitly. Once I have the config root I can then bind it to an object instance.

Here are a couple of helpers that configure configuration root and provide an instance of a configuration object - we'll use both of these methods for different purposes later:

public static IConfigurationRoot GetIConfigurationRoot(string outputPath)
{            
    return new ConfigurationBuilder()
        .SetBasePath(outputPath)
        .AddJsonFile("appsettings.json", optional: true)
        .AddUserSecrets("e3dfcccf-0cb3-423a-b302-e3e92e95c128")
        .AddEnvironmentVariables()
        .Build();
}

public static KavaDocsConfiguration GetApplicationConfiguration(string outputPath)
{
    var configuration = new KavaDocsConfiguration();

    var iConfig = GetIConfigurationRoot(outputPath);

    iConfig
        .GetSection("KavaDocs")
        .Bind(configuration);

    return configuration;
}

Notice that the code needs a basepath in order to find the appsettings.json file which is going to be the output path for the file in the test project. I copied this file from my Web Project so I get the same configuration settings and then make sure I mark it as copy to the output folder:

In order for UserSecrets to work in a test project a little extra effort is required since test projects don't let you just edit the value in Visual Studio as you can in a Web Project. I added my UserSecrets key from the Web project into test project's .csproj file configuration:

<PropertyGroup>
  <TargetFramework>netcoreapp2.0</TargetFramework>
  <UserSecretsId>e4ddcccf-0cb3-423a-b302-e3e92e95c128</UserSecretsId>
</PropertyGroup>

so now I'm also looking at the same UserSecrets values that my Web project is looking at. Yay!

Using the Configuration Object Explicitly

In my test project using NUnit I can now pull this value out as part of the initialization and store it as a property on my test object:

[TestFixture]
public class SmtpTests
{
    private KavaDocsConfiguration configuration;

    [SetUp]
    public void Init()
    {
        configuration = TestHelper.GetApplicationConfiguration(TestContext.CurrentContext.TestDirectory);
    }
    
    [Test]
    public async void SendEmailTest()
    {
        var smtp = new SmtpClientNative();

        // this code here uses the configuration
        smtp.MailServer = configuration.Email.MailServer;
        smtp.Username = configuration.Email.MailServerUsername; 
        smtp.Password = configuration.Email.MailServerPassword; 

        smtp.SenderEmail = "West Wind Technologies <info@west-wind.com>";
        smtp.Recipient = "test@gmail.com";

        smtp.Message = "Hello from Mail Gun. This is a test";
        smtp.Subject = "Mailgun Test Message";

        Assert.IsTrue(await smtp.SendMailAsync(),smtp.ErrorMessage);
    }

}    

The test method then uses the configuration values and I'm off to the races. The values are read from both appSettings.json and from UserSecrets.

This works great and if all you need is a configuration object to read a few values this approach is easy and sufficient.

Setting up Dependency Injection

For the second use case I mentioned in the intro I need Configuration to come from Dependency injection in order to inject it into child objects in the dependency chain. To do this I need to a little more work setting up the Dependency provider in the configuration. The business object in this case has a number of dependencies on a EF DbContext as well as the configuration.

In order to do this I can set up the dependency injection in the initialization of the class:

public class SmtpTests 
{
    private ServiceProvider serviceProvider;
    private KavaDocsConfiguration configuration;        
    private UserBusiness userBusiness;
    
    [SetUp]
    public void Init()
    {
       configuration = TestHelper.GetApplicationConfiguration(TestContext.CurrentContext.TestDirectory);
    
       var services = new ServiceCollection();
    
       // Simple configuration object injection (no IOptions<T>)
       services.AddSingleton(configuration);
       
    
       // configure EF Core DbContext - using the configuration
       services.AddDbContext<KavaDocsContext>(builder =>
       {
           var connStr = configuration.ConnectionString;
           if (string.IsNullOrEmpty(connStr))
               connStr = "server=.;database=KavaDocs; integrated security=true;MultipleActiveResultSets=true";
    
           builder.UseSqlServer(connStr, opt =>
           {
               opt.EnableRetryOnFailure();
               opt.CommandTimeout(15);
           });
       });
       
       // has a depedency on DbContext and Configuration
       services.AddTransient<UserBusiness>();
    
       // Build the service provider
       serviceProvider = services.BuildServiceProvider();
       
       // create a userBusiness object with DI    
       userBusiness = serviceProvider.GetRequiredService<UserBusiness>();
    }
}

The code creates a services collection and adds the various dependencies needed for this particular test class. If you end up doing this for a bunch of classes this configuration code could also be moved into the test helper which could return an object with all the dependencies.

So this code adds the configuration, a DbContext, and a business object into the service provider.

In my business object I have a method that handles the email sending I showed earlier internally and I can now load a user and run an integration test sending a validation key:

[Test]
public void UserSendEmail()
{
    // connection string should be set from config
    var user = userBusiness.GetUser(TestHelper.UserId1);
    var validationKey = user.ValidationKey;

    Assert.IsTrue(userBusiness.ValidateEmail(validationKey));
}

And there you have it - injected values in your tests.

IOptions instead of raw Configuration

If you'd rather inject IOptions<T> rather than the raw configuration instance you can change the Init() code slightly and use the following:

var services = new ServiceCollection();

// IOption configuration injection
services.AddOptions();

var configurationRoot = TestHelper.GetIConfigurationRoot(TestContext.CurrentContext.TestDirectory);
services.Configure<KavaDocsConfiguration>(configurationRoot.GetSection("KavaDocs"));
...

serviceProvider = services.BuildServiceProvider();

// to use (or store in )
iConfig = serviceProvider.GetRequiredService<IOptions<KavaDocsConfiguration>>()
var server = iConfig.Value.Email.MailServer;

Usually I try to avoid IOptions<T> for the sheer ugliness of the intermediate interface, and unless you need the specific features of IOptions<T> (see my previous article) I much rather just use the raw configuration object.

Summary

Using Configuration in non-ASP.NET projects is not real obvious or at least it wasn't for me so hopefully this post provides a simple overview on how you can get the same configuration you might be using in your main application to also work inside of your test or other non-ASP.NET Projects.

this post created and published with Markdown Monster
Posted in .NET  ASP.NET  

The Voices of Reason


 

Kevin Rich
February 19, 2018

# re: Accessing Configuration in .NET Core Test Projects

This is great. I was goofing with this issue just last week, but hadn't gotten around to flushing out a solution. Which packages (or metapackages) are you using in your test project for the configurations? Microsoft.Extensions.Configuration doesn't seem to cover all of the extensions methods for the ConfigurationBuilder.


Jav
February 19, 2018

# re: Accessing Configuration in .NET Core Test Projects

Thanks. Useful info for integration test scenarios (as per you example) but worth pointing out (to others) that this should not be necessary in unit tests and if you need config files and DI for these kinds of tests then you are probably doing something wrong.


Dmitry Pavlov
February 19, 2018

# re: Accessing Configuration in .NET Core Test Projects

I decided to just use my "Testing" environment but with real host. This way I can access any injected service (including configuration related ones) via GetService(). Of course in this case the tests are integration ones rather than unit tests, but for unit tests it's anyway mocking should be used instead of accessing configuration files.

public class BaseTestServerDependent 
{
    public TestServer Server { get; }
    public HttpClient Client { get; }

    public TestServerDependent()
    {
        ServiceCollectionExtensions.UseStaticRegistration = false;
        var hostBuilder = new WebHostBuilder()
            .UseEnvironment("Testing")
            .UseStartup<Startup>();

        Server = new TestServer(hostBuilder);
        Client = Server.CreateClient();
    }

    public TService GetService<TService>()
        where TService : class
    {
        return Server?.Host?.Services?.GetService(typeof(TService)) as TService;
    }
}

As an example of the test class:

public class MyTests : TestServerDependent
{
    [Test]
    public void Should_Access_Injected_Services()
    {
        var mySettings = GetService<IOptions<MySettings>>();
        Assert.NotNull(mySettings);
        Assert.NotNull(mySettings.Value);

        var myService = GetService<IMyService>();
        Assert.NotNull(service);

        // ... the rest of the test ...
    }
}

Hope that helps as well.


Rick Strahl
February 21, 2018

# re: Accessing Configuration in .NET Core Test Projects

@Jav - I'm not unit testing expert, but do you want to expand on that? How could you possibly test a component that has nested dependencies? At some point you'll have to test higher level components that have dependencies and that point you will most likely need DI to make that happen. Doing low level component unit tests w/o dependencies (or manually created dependencies) only get you so far in even a medium complex solution?


Andrew Lock
February 23, 2018

# re: Accessing Configuration in .NET Core Test Projects

Great post as always Rick. One thing, rather than copying the settings file from your web project to your test file, I'd suggest linking it. That way if your make changes to the settings file, your test project is updated at the same time. https://andrewlock.net/sharing-appsettings-json-configuration-files-between-projects-in-asp-net-core/


Rick Strahl
March 01, 2018

# re: Accessing Configuration in .NET Core Test Projects

@andrew - nice - didn't realize you could do that although I have to say I don't really see that use case. I can use UserSecrets for that scenario just as easily since multiple projects can share the same UserSecrets store on a local machine.


Thanh Doan
March 28, 2018

# re: Accessing Configuration in .NET Core Test Projects

Hi Rick,

Thank you for your great post. I followed your post and now I am able to run some tests for my configuration. I am using XUnit.

However, I am trying to test the way I managed secret/sensitive data configuration. Basically, I will have a default appsettings.json which has sensitive data configuration is empty of null like yours. I want to override those data configuration by Environment Variables. Therefore, I am using IFixture in XUnit to read an json file in order to set up the Environment variables. If I put tests into two different projects such as Dev Test in Dev project(not using IFixture) and Stagging Test in Stagging project using IFixture then they are all green. However, I put it into same project then some failed.

Any ideas for that issue?

Thanks


mmahouac
August 20, 2018

# re: Accessing Configuration in .NET Core Test Projects

Hi, Thank you for your post very usefull. I found also an easy way to get the configuration file

https://www.jerriepelser.com/blog/using-configuration-files-in-dotnet-core-unit-tests/


Justin James
August 21, 2018

# re: Accessing Configuration in .NET Core Test Projects

For .NET Core 2.1 in order for the TestHelper.GetIConfigurationRoot code to work I had to add some nuget packages. Looks like they split the different configuration options into different nuget packages. Below is the different packages that I had to add.

SetBasePath and AddJsonFile => Microsoft.Extensions.Configuration.Json

AddUserSecrets => Microsoft.Extensions.Configuration.UserSecrets

AddEnvironmentVariables => Microsoft.Extensions.Configuration.EnvironmentVariables

Susheel
September 28, 2018

# re: Accessing Configuration in .NET Core Test Projects

@Kevin,

Following additional packages are required.

Setting the base path of the app with SetBasePath. SetBasePath is made available to an app by referencing the Microsoft.Extensions.Configuration.FileExtensions package. Resolving sections of configuration files with GetSection. GetSection is made available to an app by referencing the Microsoft.Extensions.Configuration package. Binding configuration to .NET classes with Bind and Get. Bind and Get are made available to an app by referencing the Microsoft.Extensions.Configuration.Binder package. Get is available in ASP.NET Core 1.1 or later.


Alex
October 18, 2018

# re: Accessing Configuration in .NET Core Test Projects

Very useful post. Thanks Also for missing packages, the search box in MSDN is (finally) pretty handy, so queries by extension methods take you to the missing DLL.

https://docs.microsoft.com/en-gb/dotnet/api/index?view=aspnetcore-2.1


Daniel
November 07, 2019

# re: Accessing Configuration in .NET Core Test Projects

I thought this post is about accessing configuration in tests but the quite important method TestHelper.GetApplicationConfiguration is not shown, no link to github repo anything


Rick Strahl
November 07, 2019

# re: Accessing Configuration in .NET Core Test Projects

@Daniel - Uhm - it's there. First C# code snippet.


Coy Meeks
January 07, 2020

# re: Accessing Configuration in .NET Core Test Projects

Good stuff, Rick! To heck with mocking IConfig when you can just use the real thing! 😃

I wanted to check with you to see if you've ported this to work with Xunit. On my end, it doesn't appear that you need to specify the outputPath of the config and I wanted to confirm there would be no issues. Thanks again!


Franz
February 08, 2020

# re: Accessing Configuration in .NET Core Test Projects

Hi,

I was wondering if what is explained in this article would be a solution for another kind of project than a Test.

I basically need to access User Secrets in a DAL project that is referenced into an Asp.Net Core project.

It's basically to provide my DAL with a connectionString and InvariantName. That seems "a lot of work" for two strings value..

What would you advise?

Thank you

ps : I am new to development


Rick Strahl
February 08, 2020

# re: Accessing Configuration in .NET Core Test Projects

@Franz - you should not use UserSecrets in production since it won't be available on a deployed machine most likely. UserSecrets is just available on the local dev machine and it works because it's only part of the IConfiguration chain of providers. IOW, in development the value is caught first from UserSecrets but in production, no user secrets so the configuration is actually pulled from the configuration file (or whatever other providers like Azure Key Vault) are set up.

You can roll your own configuration manager and store things in a JSON file, but the config system in .NET does all that for you. The downside is that if DI is not auto-provided you have to configure the configuration yourself as outlined in this post.


Jason
February 19, 2020

# re: Accessing Configuration in .NET Core Test Projects

Great post - this is exactly what I've done with my test project, which is being used to drive a number of BDD tests with Specflow. Question though, although this is good from the perspective of associating interfaces with classes and for managing lifetimes and singletons etc. it feels like its only half-working since there is no corresponding constructor injection etc. Resolving dependencies still requires either ActivatorUtilities or the Service locator (anti)pattern? Is it possible to turn on the automatic resolution of these interfaces through e.g. Constructor injection in an test project?


Rick Strahl
February 20, 2020

# re: Accessing Configuration in .NET Core Test Projects

@Jason - I think we need to roll our own and provide our own abstraction as I did in the example here.

It sure would be nice if this was baked in somehow, but given that you can use all sorts of different testing frameworks to run .NET Core test, I think we'll be stuck with the hand rolled approach.

Microsoft could provide a default implementation of test helpers that could be used, but the testing space is so opinionated and people likely would just complain that it doesn't do this or that being a constant source of noise. Building a basic test helper like the above isn't a big deal and it's a one time thing per project so not so bad...


Chris Macgowan
May 04, 2020

# re: Accessing Configuration in .NET Core Test Projects

Hi - Cool implementation. I have been trying to implement appsetting.json for AppConfig and did not know it was not supported in the VS2019 Test Project. Thanks for your project 😃

Everything is building - where do I put the data objects (see below) I need to implement this somewhere correct?

    public class Macgowan
    {
        public string ApplicationName { get; set; }
        public string ConnectionString { get; set; }
        public string ApplicationBasePath { get; set; }
        public string ApplicationHomeUrl { get; set; }

        public Email email;
    }

    public class Email
    {
        public string MailServer { get; set; }
        public string MailServerUsername { get; set; }
        public string MailServerPassword { get; set; }
        public string SenderName { get; set; }
        public string SenderEmail { get; set; }
        public string AdminSenderEmail { get; set; }
        public Boolean UseSsl { get; set; }
    }

I'm going to post the rest of the implementation just for reference.


    public class AppConfiguration
    {
        // We are testing this >>>
        public static IConfigurationRoot configuration;

        private IConfigurationRoot GetIConfigurationRoot()
        {
            try
            {
                // Build configuration
                configuration = new ConfigurationBuilder()
                    .SetBasePath(GetConfigurationFilePath())
                    .AddJsonFile("appsettings.json", optional: true)
                    .Build();
            }
            catch (Exception e)
            {
                // Error loading the application configuration file 
            }

            return configuration; 
        }


        public AppConfiguration GetApplicationConfiguration()
        {
            var configuration = new AppConfiguration();

            var iConfig = GetIConfigurationRoot();

            iConfig
                .GetSection("Macgowan")
                .Bind(configuration);

            return configuration;
        }
    }

This is the test implementation


    public class BaseFramework
    {
        private AppConfiguration appConfiguration;

        public BaseFramework()
        {

            appConfiguration = new AppConfiguration();
            appConfiguration = appConfiguration.GetApplicationConfiguration();

            var temp = appConfiguration.Macgowan.ApplicationName;

        }
    }

Thanks for your help in advance

Ciao, Chris


chris macgowan
May 05, 2020

# re: Accessing Configuration in .NET Core Test Projects

Hi - Cool implementation. I have been trying to implement appsetting.json for AppConfig and did not know it was not supported in the VS2019 Test Project. Thanks for your project 😃

Everything is building - where do I put the data objects (see below) I need to implement this somewhere correct?

public class Macgowan
{
    public string ApplicationName { get; set; }
    public string ConnectionString { get; set; }
    public string ApplicationBasePath { get; set; }
    public string ApplicationHomeUrl { get; set; }

    public Email email;
}

public class Email
{
    public string MailServer { get; set; }
    public string MailServerUsername { get; set; }
    public string MailServerPassword { get; set; }
    public string SenderName { get; set; }
    public string SenderEmail { get; set; }
    public string AdminSenderEmail { get; set; }
    public Boolean UseSsl { get; set; }
}

I'm going to post the rest of the implementation just for reference.


public class AppConfiguration
{
    // We are testing this >>>
    public static IConfigurationRoot configuration;

    private IConfigurationRoot GetIConfigurationRoot()
    {
        try
        {
            // Build configuration
            configuration = new ConfigurationBuilder()
                .SetBasePath(GetConfigurationFilePath())
                .AddJsonFile("appsettings.json", optional: true)
                .Build();
        }
        catch (Exception e)
        {
            // Error loading the application configuration file 
        }

        return configuration; 
    }


    public AppConfiguration GetApplicationConfiguration()
    {
        var configuration = new AppConfiguration();

        var iConfig = GetIConfigurationRoot();

        iConfig
            .GetSection("Macgowan")
            .Bind(configuration);

        return configuration;
    }
}

This is the test implementation


public class BaseFramework
{
    private AppConfiguration appConfiguration;

    public BaseFramework()
    {

        appConfiguration = new AppConfiguration();
        appConfiguration = appConfiguration.GetApplicationConfiguration();

        var temp = appConfiguration.Macgowan.ApplicationName;

    }
}

Thanks for your help in advance

Ciao, Chris


chaami
November 27, 2020

# re: Accessing Configuration in .NET Core Test Projects

Hi Rick,
Thank you very much for this post, it gave me the courage to tinker around it 😉
Have you tried removing the need of using the outputPath variable and directly defining it to point the tested project appsettings with:

string outputPath = Path.GetDirectoryName(Assembly.GetAssembly(typeof(KavaDocsConfiguration).Location);

This should allow you to avoid having to copy the appsettings.*.json files.
Even if they quickly become stable, it's still a pain to have duplicated info.

Hope this helps, thanks again !
👋


F. Rigaudie
February 02, 2021

# re: Accessing Configuration in .NET Core Test Projects

Thanks for this solution, worked like a charm for us 😉


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024