3

I want to get the name of the current environment (QA/Prod/DEV) in my code, so I can then use that name when calling AddJsonFile

*Note that this code is not for an application, but instead for selenium Unit tests using the test explorer test runner with NUNIT, so I do not have a Startup.cs class or ihostingenvironment that I can use.

I thought to do this, I would need to retrieve the "Hosting:Environment" or "ASPNETCORE_ENVIRONMENT" value inside the launchsettings.json, which I thought should represent the CURRENT Visual Studio process/session/environment. See Environment.GetEnvironmentVariable("Hosting:Environment") below for how I am retreiving this launchsettings.json value.

    public static IConfiguration InitConfiguration()
    {
        LaunchSettingsFixture LaunchSettings = new LaunchSettingsFixture();

        var environmentName = Environment.GetEnvironmentVariable("Hosting:Environment");

        var builder = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{environmentName}.json", true)
            .AddEnvironmentVariables();
        var configuration = builder.Build();

        // Testing to see if the above worked
        string url = configuration["url"];
        string driverdirectory = configuration["driverdirectory"];

        return configuration;
    }

In the above code, I am expecting Environment.GetEnvironmentVariable("Hosting:Environment") to return me a string with a value of "blah" because I selected "blah" in the current process/profile/environment drop down (the drop down next to the Run button). See photo below for my current process/profile selection, and see my json text inside my launchsettings.json file below...

enter image description here

    {
    "profiles": {
          "Wikipedia.UITest": {
            "commandName": "Project"
               },
          "UAT": {
            "commandName": "Executable",
            "environmentVariables": {
               "Hosting:Environment": "UAT"
               }
           },
          "blah": {
            "commandName": "Executable",
            "environmentVariables": {
              "Hosting:Environment": "Development",
              "containerName": "Testing"
            }
          },
          "Prod": {
            "commandName": "Executable",
            "environmentVariables": {
              "Hosting:Environment": "prod"
            }
          }
        }
      }

Instead Environment.GetEnvironmentVariable("Hosting:Environment") returns me the last instance inside the launchsettings.json file (regardless of what I choose in the drop down as shown above in the screenshot). i.e. It is returning a value of "prod". Again, I want to mention that this is an NUNIT NET CORE project and I am using the Test Explorer to Run and Debug tests (See screenshot below)

enter image description here

Also, I dont know if the below code is relavent, but the below is how I set the environment variables...

    public class LaunchSettingsFixture
    {
        public LaunchSettingsFixture()
        {
            using (var file = File.OpenText("Properties\\launchSettings.json"))
            {
                var reader = new JsonTextReader(file);
                var jObject = JObject.Load(reader);

                var variables = jObject
                    .GetValue("profiles")
                    //select a proper profile here
                    .SelectMany(profiles => profiles.Children())
                    .SelectMany(profile => profile.Children<JProperty>())
                    .Where(prop => prop.Name == "environmentVariables")
                    .SelectMany(prop => prop.Value.Children<JProperty>())
                    .ToList();

                foreach (var variable in variables)
                {
                    Environment.SetEnvironmentVariable(variable.Name, variable.Value.ToString());
                }
            }
        }

    }  
Mike Johnston
  • 237
  • 6
  • 20
  • What is the value of Hosting:Environment before calling into LaunchSettingsFixture? That code doesn't seem right because its reading the file and setting things in the order they appear so it's not surprising you are getting the values from the last entry in the file. – Mike Zboray Nov 06 '19 at 21:50
  • The value before calling LaunchSettingsFixture is null. According to this answer: https://stackoverflow.com/a/43951218 launchSettings.json isn't considered by a test runner. So I have to set the environment variables first using SetEnvironmentVariable. The code provided in that answer is the code I use (you can see it above) to set the variables. It for 39 upvotes, so I dont believe it is wrong? – Mike Johnston Nov 06 '19 at 22:51
  • I'd encourage you to print out the values some where you can see them. Even in console application it can be seen that this snippet is just dumping all the variables for all of the profiles. I think the comment "select a proper profile here" there was intended to get you to write more code to select the proper profile not telling you that's what the code was doing. – Mike Zboray Nov 06 '19 at 23:07
  • I don't think that is the case because If I were to 'select a proper profile', that would be defeating the purpose of Visual Studio returning me the variables from the 'current' profile/service/environment, no? I thought that when you select the profile in the drop down (next to the Run button), then you run the program or test, Visual Studio should then be running from that profile, so then visual Studio selects the profile for me, returning the values in launchsettings on the 'current' profile' (i.e.based on what profile was chosen in the drop down) – Mike Johnston Nov 06 '19 at 23:37
  • I think its not a supported scenario. See this [issue](https://github.com/aspnet/Tooling/issues/1089). You'll probably have to build something that doesn't rely on this UI setting until they resolve passing launchsettings.json environment variables to test runners. – Mike Zboray Nov 07 '19 at 00:08

1 Answers1

3

As @Mike Zboray correctly wrote, neither VS nor Rider passes current launch profile to the test runner, so there is no way to set environment variables via launchSettings.json as of today.

Regarding LaunchSettingsFixture, it indeed goes through all the settings and add each environment variable from every profile, that's why you always see latest production configuration. Probably in the answer you referring the author implied the use of single launch profile in the project.

One possible way to approach it is to use build configuration setting and conditionally copy appsettings.*.json to the output folder. Similar to how it was done with Full .NET Framework before .NET Core.

In the project file you can configure which files to copy based on the build profile:

<ItemGroup>
  <None Update="appsettings.$(Configuration).json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
  <None Update="appsettings.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

And having required config files copied, you can add them to the configuration builder:

public static IConfiguration InitConfiguration()
{
    var settingsFiles = Directory.GetFiles(Directory.GetCurrentDirectory(), "appsettings.*.json"));
    if (settingsFiles.Length != 1) throw new Exception($"Expect to have exactly one configuration-specfic settings file, but found {string.Join(", ", settingsFiles)}.");
    var settingsFile = settingsFiles.First();

    var builder = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile(settingsFile)
        .AddEnvironmentVariables();
    var configuration = builder.Build();

    // Testing to see if the above worked
    string url = configuration["url"];
    string driverdirectory = configuration["driverdirectory"];

    return configuration;
}

Remember that now you have to change Build Configuration instead of the Launch Profile to run tests against the environment of choice.

Andrii Litvinov
  • 12,402
  • 3
  • 52
  • 59