DEV Community

andrea
andrea

Posted on

Using dotnet IConfiguration inside xUnit projects

In a .NET core XUnit project, configuration functionality is not readily available.

My need is using secrets inside UI tests (i.e. for signing in before testing the web app) without committing them into the repository.

I read different solutions and found many third party libraries but in the end I chose a simple approach, a few lines of code easily adaptable to different needs, still functional both for local development and CI/CD environments.

I created a simple github repo for demonstration purposes.

Basically there are 2 files:

  • appsettings.json - you can commit it, but without sensitive data. Here you put your config, using placeholders for sensitive data.
  • appsettings.local.json - you must not commit it, list it in your .gitignore file. Here you put you sensitive data, overriding placeholders from appsettings.json.

Then you have to copy those files to output directory, adding this to your .csproj:

    <ItemGroup Condition="'$(Configuration)' == 'Debug'">
        <None Update="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
    </ItemGroup>
    <ItemGroup Condition="'$(Configuration)' == 'Debug'">
        <None Update="appsettings.local.json" CopyToOutputDirectory="PreserveNewest" />
    </ItemGroup>
Enter fullscreen mode Exit fullscreen mode

Now the code.

Create an utilty class (TestConfigHelper) for setting up IConfiguration in every test (you can adjust this class as needed, an example will be reported in the following).

This is where you configure the Configuration service:

public static IConfigurationRoot GetIConfigurationRoot()
{
    return new ConfigurationBuilder()
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile("appsettings.local.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables()
        .Build();
}
Enter fullscreen mode Exit fullscreen mode

In this code we take out configuration from three places:

  • first appsetting.json
  • then appsetting.local.json
  • in the end from the environment

The order matters. appsetting.local.json will override appsetting.json, and environment variables will override everything.

In this way you can use appsetting.local.json for local development, but also inject sensitive data through environment variables in you CI/CD pipeline.

Your tests should inherit from a BaseTestClass which loads configuration invoking TestConfigHelper::GetIConfigurationRoot method.

And that's all. You can now access the configuration using the object returned from GetIConfigurationRoot in every test.

Bonus #1 - configuration class

I like creating a class representing my application configuration, and bind it to the configuration.
TestConfigHelper has an example of this. The code is like this (where MyAppConfig is the class representing the configuration, stored under "MyApp" root node in the configuration file):

public static MyAppConfig GetMyAppConfiguration()
{
    var configuration = new MyAppConfig();
    GetIConfigurationRoot().Bind("MyApp", configuration);
    return configuration;
}
Enter fullscreen mode Exit fullscreen mode

Bonus #2: user secrets

Moreover, for local development, you can configure user secrets. This requires some more configuration.

  • Creating and setting a secret
dotnet user-secrets init
dotnet user-secrets set key value
Enter fullscreen mode Exit fullscreen mode
  • Adding the user secrets key to the .csproj file:
<UserSecretsId>your-secret-id-here</UserSecretsId>
Enter fullscreen mode Exit fullscreen mode
  • Configuring also Secrets in TestConfigHelper.cs, something like this:
public static IConfigurationRoot GetIConfigurationRoot()
{
    return new ConfigurationBuilder()
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile("appsettings.local.json", optional: true, reloadOnChange: true)
        .AddUserSecrets("e3dfcccf-0cb3-423a-b302-e3e92e95c128")         // <-- this is the new line !!
        .AddEnvironmentVariables()
        .Build();
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)