6
votes

I am running unit tests as part of an ASP.NET Core MVC solution. The version of .NET Core is 2.1 to be exact.

I have created a profile section in a launchSettings.json file that contains environment variables that I would like to have loaded and injected by the test runner, so that the environment variables are available and can contain specific values when running unit tests.

The launchSettings.json from my ASP.NET Core MVC project is added to my unit test project as a link, and the properties are set to Build Action - None, Copy to output folder - Copy Always.

The file gets copied to my output folder, but I'm not sure how to get the test runner to use this file with the UnitTesting profile. I have tried using the word "Test" as the profile name, nothing seems to work.

Here is a sample launchSettings.json file:

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:62267/",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "MigrationHistoryTableName": "MigrationHistory",
        "ConnectionStringName": "EFConnectionString",
        "Redis__SSL": "True",
        "Redis__Port": "6380",
        "Redis__InstanceName": "RedisDev",
        "Redis__AbortConnect": "False",
        "Redis__ConnectionString": "{URI}:{Port},password={Password},ssl={SSL},abortConnect={AbortConnect}"
      }
    },
    "MyDataServices": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "MigrationHistoryTableName": "MigrationHistory",
        "ConnectionStringName": "EFConnectionString",
        "Redis__SSL": "True",
        "Redis__Port": "6380",
        "Redis__InstanceName": "RedisDev",
        "Redis__AbortConnect": "False",
        "Redis__ConnectionString": "{URI}:{Port},password={Password},ssl={SSL},abortConnect={AbortConnect}"
      },
      "applicationUrl": "http://localhost:4080/"
    },
    "UnitTesting": {
      "commandName": "Executable",
      "executablePath": "test",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "MigrationHistoryTableName": "MigrationHistory",
        "ConnectionStringName": "EFConnectionString",
        "Redis__SSL": "True",
        "Redis__Port": "6380",
        "Redis__InstanceName": "RedisDev",
        "Redis__AbortConnect": "False",
        "Redis__ConnectionString": "{URI}:{Port},password={Password},ssl={SSL},abortConnect={AbortConnect}"
      }
    }
  }
}

I understand that by default the launchSettings.json file is ignored in git. We will check in our code with a development version of this file that will contain an example of the settings that are expected to be available in a development environment.

Thank you kindly for taking the time to read this and help out!

3
For the time being, I have created a method that loads the profile from file and sets the environment variables as part of the test Setup fixture, but I would rather let the framework do this for me if it is possible. - Yves Rochon
Do you want to get the specific UnitTesting profile ? - Gabriel Guarnieri Cardoso
Yes that would be fantastic. - Yves Rochon

3 Answers

1
votes

I wrote this static loader for now, but that's not ideal:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace MyNamespaceHere.Services.Common.Utilities
{
    public static class LaunchSettingsLoader
    {
        public static void LaunchSettingsFixture(string launchSettingsPath = "Properties\\launchSettings.json", string profileName = "UnitTesting")
        {
            using (var file = File.OpenText(launchSettingsPath))
            {
                var reader = new JsonTextReader(file);
                var jObject = JObject.Load(reader);

                var allprofiles = jObject
                    .GetValue("profiles", StringComparison.OrdinalIgnoreCase);

                // ideally we use this
                var variables = jObject
                    .GetValue("profiles", StringComparison.OrdinalIgnoreCase)
                    //select a proper profile here
                    .SelectMany(profiles => profiles.Children())
                    //.Where(p => p.Value<String> == profileName)
                    .SelectMany(profile => profile.Children<JProperty>())
                    .Where(prop => prop.Name == "environmentVariables")
                    .SelectMany(prop => prop.Value.Children<JProperty>())
                    .ToList();

                Console.WriteLine(variables?.Count);

                var profilesDictJToken = allprofiles.ToObject<Dictionary<string, JToken>>();
                var unitTestingProfile = profilesDictJToken[profileName];
                var unitTestingProfileDictJToken = unitTestingProfile.ToObject<Dictionary<string, JToken>>();
                var environmentVariables = unitTestingProfileDictJToken["environmentVariables"];
                var environmentVariablesList = environmentVariables.ToList();

                foreach (var variable in environmentVariablesList)
                {
                    var name = ((JProperty)variable).Name;
                    var value = ((JProperty)variable).Value.ToString();
                    Environment.SetEnvironmentVariable(name, value);
                }
            }
        }
    }
}
0
votes

I have created a package that might be able to help you: https://www.nuget.org/packages/DotNet.Project.LaunchSettings

You can find the source here: https://github.com/andygjp/DotNet.Project.LaunchSettings

I have a similar use case to you - I want to test different environments and I keep the logon details to these environments in the launchSettings.json.

This is how I use it:

async Task Example()
{
  var launchSettings = VisualStudioLaunchSettings.FromCaller();
  var profiles = launchSettings.GetProfiles();
  var profile = profiles.FirstOrEmpty();

  var client = await AppEnvironment.FromEnvironment(profile.EnvironmentVariables).CreateClient();
  // use the client
}

It assumes launchSettings.json is in the usual place:

MyProject -> Properties -> launchSettings.json

However, I could add the functionality to load launch settings from a specified file path if you would find it useful.

The example above uses the first profile it finds, however, you can also use a particular profile.

Hope it helps.

0
votes

Another solution for using environment variables in unit tests, for either mstest or xunittest, is through the ".runsettings" file provided for the platform:

See link below:

https://stackoverflow.com/a/68531200/4593944