Reputation:
Given the following example helper
public static class RuntimeHelper
{
public static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
public static bool IsMac => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
}
There are several questions
A sample implementation might be
public sealed class RuntimeHelperTests
{
// IF RUNNING ON WINDOWS
[Fact]
public void TheOSShouldBeWindows()
{
Assert.True(RuntimeHelper.IsWindows);
}
[Fact]
public void TheOSShouldNotBeLinux()
{
Assert.False(RuntimeHelper.IsLinux);
}
[Fact]
public void TheOSShouldNotBeMac()
{
Assert.False(RuntimeHelper.IsMac);
}
// DO THIS FOR THE OTHER OPERATING SYSTEMS TOO
}
But those tests only pass when running on a Windows machine...
Upvotes: 0
Views: 1676
Reputation: 13783
We can write a lot of layers in our logic. There is no practical upper limit on that. However, no matter how many layers we write, there's always one layer on the outside, which has to interact with the real world (e.g. a monitor, network, OS, file system, IO device, ...)
It is the nature of the beast that the last layer can never truly be unit tested, because its dependencies are external resources. All of the other layers (i.e. not the last one) can be tested, because their neighboring layer can be mocked.
In your case, RuntimeInformation
is your unmockable real world dependency. This is one of the main issues with static
in testing.
Generally speaking, the advice is to then separate the business logic from the external dependency, so that you can test the business logic while wrapping the external dependency in a nice mockable layer of its own. Here is an answer of mine elaborating on exactly that.
However, in your case, there is no real business logic. Your RuntimeHelper
is already that abstracted wrapper.
It's not meaningful to write tests for RuntimeHelper
.
Had you made RuntimeHelper
instanced and not static, which I strongly recommend, then you would be able to unit test the classes that depend on RuntimeHelper
by injecting a mocked RuntimeHelper
which can pretend like you're on any particular OS, even when you are not.
How to run specific unit tests only when running on a specific operating system?
You're asking the wrong question. Rather than writing different tests for running on actually different OS'es, you should be writing the same (behavioral) tests, but in each test set the mocked RuntimeHelper
to claim that you're working in a different OS.
For example:
[Fact]
public void FilePathService_returns_correct_linux_path()
{
var mockedRuntimeHelper = new MockedRuntimeHelper(OS.Linux);
var filePathService = new FilePathService(mockedRuntimeHelper);
var result = filePathService.GetConfigurationFolder();
result.Should().Be("/path/to/config");
}
[Fact]
public void FilePathService_returns_correct_windows_path()
{
var mockedRuntimeHelper = new MockedRuntimeHelper(OS.Windows);
var filePathService = new FilePathService(mockedRuntimeHelper);
var result = filePathService.GetConfigurationFolder();
result.Should().Be(@"C:\path\to\config");
}
[Fact]
public void FilePathService_returns_correct_mac_path()
{
var mockedRuntimeHelper = new MockedRuntimeHelper(OS.Mac);
var filePathService = new FilePathService(mockedRuntimeHelper);
var result = filePathService.GetConfigurationFolder();
result.Should().Be(@"whatever a Mac path is, I don't know");
}
This enables you to test all three behaviors running on any OS.
It's still a good idea to test your code on all OS's just to be safe that there are no weird edge cases with your runtime, but that is unrelated to wanting to write specific unit tests that should only be run on specific OSes.
Upvotes: 4