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.
Add a <PackageReference /> to the EnvironmentAbstractions package:
<PackageReferenceInclude="EnvironmentAbstractions"Version="1.0.0" />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);}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);}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.