Skip to content

Repository files navigation

System.Environment Abstractions for .NET

NuGet package EnvironmentAbstractions (with prereleases)NuGet package EnvironmentAbstractions.TestHelpers (with prereleases)NuGet package EnvironmentAbstractions.BannedApiAnalyzer (with prereleases)Official Build

EnvironmentAbstractions is an interface abstraction for the System.Environment class in .NET to make testing components easier. A lot of repositories have code that accesses environment variables and corresponding logic to mock the functionality in a unit test. Some people set environment variables during tests while others have their own interfaces. However, setting environment variables applies to the entire process which can limit the parallelism of test execution. This API is similar to System.IO.Abstractions but intended for environment variable access.

There are two interfaces available, IEnvironmentProvider and IEnvironmentVariableProvider. The IEnvironmentProvider abstracts away everything in System.Environment while IEnvironmentVariableProvider only provides abstractions for accessing environment variables.

Getting Started

Add a <PackageReference /> to the EnvironmentAbstractions package:

<PackageReferenceInclude="EnvironmentAbstractions"Version="1.0.0" />

IEnvironmentProvider

The IEnvironmentProvider interface provides an abstraction for everything in the System.Environment class including getting system information, user information, or accessing environment variables.

Use the IEnvironmentProvider interface when accessing the environment your code:

publicstaticvoidSayHello(IEnvironmentProviderenvironmentProvider){Console.WriteLine("Hello, {0}!",environmentProvider.UserName);}publicstaticvoidPrintSortedEnvironmentVariables(IEnvironmentProviderenvironmentProvider){foreach(KeyValuePair<string,string>iteminenvironmentProvider.GetEnvironmentVariables().OrderBy(i =>i.Key)){Console.WriteLine("{0}={1}",item.Key,item.Value);}}

Then you can call that code with the default environment variable provider SystemEnvironmentProvider and its singleton SystemEnvironmentProvider.Instance:

publicstaticvoidMain(string[]args){IEnvironmentProviderenvironmentProvider=SystemEnvironmentProvider.Instance;SayHello(environmentProvider);PrintSortedEnvironmentVariables(environmentProvider);}publicstaticvoidSayHello(IEnvironmentProviderenvironmentProvider){Console.WriteLine("Hello, {0}!",environmentProvider.UserName);}publicstaticvoidPrintSortedEnvironmentVariables(IEnvironmentProviderenvironmentProvider){foreach(KeyValuePair<string,string>iteminenvironmentProvider.GetEnvironmentVariables().OrderBy(i =>i.Key)){Console.WriteLine("{0}={1}",item.Key,item.Value);}}

Unit tests can use the MockEnvironmentProvider class from the EnvironmentAbstractions.TestHelpers package to mock the values returned by environment access:

usingEnvironmentAbstractions.TestHelpers;usingXunit;[Fact]publicvoidTest1(){IEnvironmentProviderenvironmentProvider=newMockEnvironmentProvider();environmentVariableProvider["Variable1"]="Value1";environmentVariableProvider["Variable2"]="Value2";Program.PrintSortedEnvironmentVariables(environmentVariableProvider);}[Fact]publicvoidTest2(){IEnvironmentProviderenvironmentProvider=newMockEnvironmentProvider();environmentProvider.UserName="UserA";Program.SayHello(environmentVariableProvider);}

Unit tests can also use any system capable of mocking interfaces like Moq

usingMoq;[Fact]publicvoidGetEnvironmentVariableMoqTest(){Mock<IEnvironmentProvider>environmentProviderMock=newMock<IEnvironmentProvider>();environmentProviderMock.Setup(i =>i.GetEnvironmentVariable(It.Is<string>(i =>i.Equals("Var1")))).Returns("Value1");environmentProviderMock.Setup(i =>i.GetEnvironmentVariable(It.Is<string>(i =>i.Equals("Var2")))).Returns("Value2");IEnvironmentProviderenvironmentProvider=environmentProviderMock.Object;Program.PrintSortedEnvironmentVariables(environmentProvider);}

IEnvironmentVariableProvider

If your project only wants to abstract environment variable access, you can use only the IEnvironmentVariableProvider interface.

Use the IEnvironmentVariableProvider interface when accessing environment variables in your code:

publicstaticvoidPrintSortedEnvironmentVariables(IEnvironmentVariableProviderenvironmentVariableProvider){foreach(KeyValuePair<string,string>iteminenvironmentVariableProvider.GetEnvironmentVariables().OrderBy(i =>i.Key)){Console.WriteLine("{0}={1}",item.Key,item.Value);}}

Then you can call that code with the default environment variable provider SystemEnvironmentVariableProvider and its singleton SystemEnvironmentVariableProvider.Instance:

publicstaticvoidMain(string[]args){PrintSortedEnvironmentVariables(SystemEnvironmentVariableProvider.Instance);}publicstaticvoidPrintSortedEnvironmentVariables(IEnvironmentVariableProviderenvironmentVariableProvider){foreach(KeyValuePair<string,string>iteminenvironmentVariableProvider.GetEnvironmentVariables().OrderBy(i =>i.Key)){Console.WriteLine("{0}={1}",item.Key,item.Value);}}

Unit tests can use the MockEnvironmentVariableProvider class from the EnvironmentAbstractions.TestHelpers package to mock environment variables:

usingEnvironmentAbstractions.TestHelpers;usingXunit;[Fact]publicvoidTest1(){IEnvironmentVariableProviderenvironmentVariableProvider=newMockEnvironmentVariableProvider();environmentVariableProvider["Variable1"]="Value1";environmentVariableProvider["Variable2"]="Value2";Program.PrintSortedEnvironmentVariables(environmentVariableProvider);}

Unit tests can also use any system capable of mocking interfaces like Moq

usingMoq;[Fact]publicvoidGetEnvironmentVariableMoqTest(){Mock<IEnvironmentVariableProvider>environmentVariableProviderMock=newMock<IEnvironmentVariableProvider>();environmentVariableProviderMock.Setup(i =>i.GetEnvironmentVariable(It.Is<string>(i =>i.Equals("Var1")))).Returns("Value1");environmentVariableProviderMock.Setup(i =>i.GetEnvironmentVariable(It.Is<string>(i =>i.Equals("Var2")))).Returns("Value2");IEnvironmentVariableProviderenvironmentVariableProvider=environmentVariableProviderMock.Object;Program.PrintSortedEnvironmentVariables(environmentVariableProvider);}

Preventing usage of System.Environment

If you want to use 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

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

Sample source code

publicstaticvoidMain(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 IEnvironmentProvider.GetEnvironmentVariable(string) instead.

About

Interface abstraction for accessing environment variables in .NET

Resources

Stars

22 stars

Watchers

2 watching

Forks

Releases

Used by

Contributors

Languages