EnvironmentAbstractions 5.0.0

dotnet add package EnvironmentAbstractions --version 5.0.0                
NuGet\Install-Package EnvironmentAbstractions -Version 5.0.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EnvironmentAbstractions" Version="5.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add EnvironmentAbstractions --version 5.0.0                
#r "nuget: EnvironmentAbstractions, 5.0.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install EnvironmentAbstractions as a Cake Addin
#addin nuget:?package=EnvironmentAbstractions&version=5.0.0

// Install EnvironmentAbstractions as a Cake Tool
#tool nuget:?package=EnvironmentAbstractions&version=5.0.0                

EnvironmentAbstractions

Nuget (with prereleases)

The EnvironmentAbstractions package provides an interface, IEnvironmentProvider, that can be used as a layer of abstraction for code to be easier to mock for unit testing.

IEnvironmentProvider

The IEnvironmentProvider interfaces supports the following members:

public interface IEnvironmentProvider : IEnvironmentVariableProvider
{
    string CommandLine { get; }

    string CurrentDirectory { get; set; }

    int CurrentManagedThreadId { get; }

    int ExitCode { get; }

    bool HasShutdownStarted { get; }

    bool Is64BitOperatingSystem { get; }

    bool Is64BitProcess { get; }

    string MachineName { get; }

    string NewLine { get; }

    OperatingSystem OSVersion { get; }

#if NET5_0_OR_GREATER
    int ProcessId { get; }
#endif

    int ProcessorCount { get; }

#if NET6_0_OR_GREATER
    string? ProcessPath { get; }
#endif

    string StackTrace { get; }

    string SystemDirectory { get; }

    int SystemPageSize { get; }

    int TickCount { get; }

#if NETCOREAPP3_1_OR_GREATER
    long TickCount64 { get; }
#endif

    string UserDomainName { get; }

    bool UserInteractive { get; }

    string UserName { get; }

    Version Version { get; }

    long WorkingSet { get; }

    void Exit(int exitCode);

    void FailFast(string? message);

    void FailFast(string? message, Exception? exception);

    string? ExpandEnvironmentVariables(string name);

    string? GetEnvironmentVariable(string name, EnvironmentVariableTarget target);

    string? GetEnvironmentVariable(string name);

    IReadOnlyDictionary<string, string> GetEnvironmentVariables();

    IReadOnlyDictionary<string, string> GetEnvironmentVariables(EnvironmentVariableTarget target);

    string[] GetCommandLineArgs();

    string GetFolderPath(Environment.SpecialFolder folder);

    string GetFolderPath(Environment.SpecialFolder folder, Environment.SpecialFolderOption option);

    string[] GetLogicalDrives();

    void SetEnvironmentVariable(string name, string? value);

    void SetEnvironmentVariable(string name, string? value, EnvironmentVariableTarget target);
}

IEnvironmentVariableProvider

The IEnvironmentVariableProvider interface is just for abstracting away access to environment variables and supports the following methods:

public interface IEnvironmentVariableProvider
{
    string? ExpandEnvironmentVariables(string name);

    string? GetEnvironmentVariable(string name, EnvironmentVariableTarget target);

    string? GetEnvironmentVariable(string name);

    IReadOnlyDictionary<string, string> GetEnvironmentVariables();

    IReadOnlyDictionary<string, string> GetEnvironmentVariables(EnvironmentVariableTarget target);

    void SetEnvironmentVariable(string name, string? value);

    void SetEnvironmentVariable(string name, string? value, EnvironmentVariableTarget target);
}

Examples

The example below shows how to use IEnvironmentProvider in a class to access the environment. You should have two constructors: one that does default logic and another that accepts an IEnvironmentProvider instance. The constructor that accepts an IEnvironmentProvider can be internal so it can only be called by unit tests.

public class MyClass
{
    private readonly IEnvironmentProvider environmentProvider;

    /// <summary>
    /// Internal constructor only for unit tests which accesses the environment from the specified IEnvironmentProvider.
    /// </summary>
    internal MyClass(IEnvironmentProvider environmentProvider)
    {
        this.environmentProvider = environmentProvider
    }

    /// <summary>
    /// Initializes a new instance of the MyClass class which accesses the environment directly from the system.
    /// </summary>
    public MyClass()
      : this(SystemEnvironmentProvider.Instance)
    {
    }

    public void SayHello()
    {
        // Get the current username from IEnvironmentProvider so that a test can mock the value
        Console.WriteLine("Hello {0}!", environmentProvider.UserName);
    }

    public CustomConfig GetConfiguration()
    {
        // Check if the user has set an environment variable to override the default location
        string customConfigLocation = environmentProvider.GetEnvironmentVariable("CUSTOM_CONFIG");

        if (!string.IsNullOrWhitespace(environmentProvider))
        {
            return LoadConfigFromLocation(customConfigLocation);
        }

        // Load the configuration from the default location, %UserProfile%\configuration.xml
        return LoadConfiguationFromLocation(Path.Combine(environmentProvider.GetEnvironmentVariable("USERPROFILE"), "configuration.xml"));
    }
}

Alternatively, you can use the IEnvironmentVariableProvider interface in a class to only abstract away access to environment variables. Like the example above, you should have two constructors: one that does default logic and another that accepts an IEnvironmentVariableProvider instance. The constructor that accepts an IEnvironmentVariableProvider can be internal so it can only be called by unit tests.

public class MyClass
{
    private readonly IEnvironmentVariableProvider environmentVariableProvider;

    /// <summary>
    /// Internal constructor only for unit tests which reads environment variables from the specified IEnvironmentVariableProvider.
    /// </summary>
    internal MyClass(IEnvironmentVariableProvider environmentVariableProvider)
    {
        this.environmentVariableProvider = environmentVariableProvider
    }

    /// <summary>
    /// Initializes a new instance of the MyClass class which reads environment variables directly from the system.
    /// </summary>
    public MyClass()
      : this(SystemEnvironmentVariableProvider.Instance)
    {
    }

    public CustomConfig GetConfiguration()
    {
        // Check if the user has set an environment variable to override the default location
        string customConfigLocation = environmentVariableProvider.GetEnvironmentVariable("CUSTOM_CONFIG");

        if (!string.IsNullOrWhitespace(environmentVariableProvider))
        {
            return LoadConfigFromLocation(customConfigLocation);
        }

        // Load the configuration from the default location, %UserProfile%\configuration.xml
        return LoadConfiguationFromLocation(Path.Combine(environmentVariableProvider.GetEnvironmentVariable("USERPROFILE"), "configuration.xml"));
    }
}

Unit tests can then use the MockEnvironmentProvider or MockEnvironmentVariableProvider class from the EnvironmentAbstractions.TestHelpers package, an existing mocking framework like Moq, or any custom implementation.

MockEnvironmentProvider

[Fact]
public void MethodReturnsExpectedValue()
{
    IEnvironmentProvider environmentProvider = new MockEnvironmentProvider
    {
        // Initializes this instance to have the UserName property return "UserA"
        UserName = "UserA",
    };

    // Sets an environment variable value
    environmentProvider["Variable1"] = "Value1";

    MyClass instance = new MyClass(environmentVariableProvider);

    string actualUsername = instance.GetUserName();

    Assert.Equal(actualUsername, "UserA");
}

MockEnvironmentVariableProvider

[Fact]
public void MethodReturnsExpectedValue()
{
    IEnvironmentVariableProvider environmentVariableProvider = new MockEnvironmentVariableProvider

    environmentVariableProvider["Variable1"] = "Value1";

    MyClass instance = new MyClass(environmentVariableProvider);

    string value = instance.Method();
}

Preventing usage of System.Environment

If you want to use IEnvironmentProvider or IEnvironmentVariableProvider exclusively in your repository, you can reference the EnvironmentAbstractions.BannedApiAnalyzer package which uses the Microsoft.CodeAnalysis.BannedApiAnalyzers Roslyn analyzer to prevent code from accessing the System.Environment APIs.

Sample project

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="EnvironmentAbstractions.BannedApiAnalyzer" Version="1.0.0" />
  </ItemGroup>
</Project>

Sample source code

public static void Main(string[] args)
{
    Console.WriteLine("Hello, {0}!", System.Environment.GetEnvironmentVariable("USERNAME"));
}

Sample error

warning RS0030: The symbol 'Environment.GetEnvironmentVariable(string)' is banned in this project: Use IEnvironmentVariableProvider.GetEnvironmentVariable(string) instead.
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.
  • net6.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on EnvironmentAbstractions:

Package Downloads
EnvironmentAbstractions.TestHelpers

Provides implementations of IEnvironmentVariableProvider so that unit tests can mock calls that retrieve environment variable.

EnvironmentAbstractions.BannedApiAnalyzer

Adds rules for Microsoft.CodeAnalysis.BannedApiAnalyzers to ensure projects don't use the System.Environment class to manipulate environment variables..

File.TestLogger

Provides a logger for the Visual Studio Test Platform that writes output to a file.

GitHub repositories (2)

Showing the top 2 popular GitHub repositories that depend on EnvironmentAbstractions:

Repository Stars
microsoft/slngen
Visual Studio solution generator
chickensoft-games/GameDemo
The Chickensoft Game Demo — a fully tested, third-person 3D game built with Godot and C#. Now with saving and loading!
Version Downloads Last updated
5.0.0 889 9/9/2024
4.0.0 16,918 5/28/2024
3.0.2 6,242 11/15/2023
2.1.0 810 9/26/2023