netstandard
is an API, not a platform. Due to the way builds and dependency resolution work today,
xUnit.net test projects must target a platform (desktop CLR, .NET Core, etc.) and run with a platform-specific
runner application.
When .NET was first shipped, there was no difference between platform and API: you directly compiled your binaries against--and ran with--the desktop CLR assemblies. The easy days. :)
As .NET started to branch out into other platforms (like Silverlight and Windows Phone), the team decided to address the desire to have a single class library which could support multiple target platforms. Portable Class Libraries (PCLs) were invented, and we saw a difference drawn between reference assemblies and runtime assemblies. PCLs allowed you to pick which platforms you needed to support, and provided reference assemblies that contained the subset of APIs that were available on all the target platforms. When the code ran on the target platform, the runtime assemblies provided the platform-specific implementation of the feature(s) in question.
The important thing to remember here is that "portable" was a concept that only applied to class libraries. It did not apply to applications, which were still required to target a specific platform; meaning, there was no such thing as a "portable console application". It can also be a little confusing with .NET platform vs. operating system platform: being "cross platform" here means supporting multiple CLR implementations, not necessarily multiple operating systems. You can target a single .NET platform (.NET Core) which runs on multiple operating system platforms (Windows, Linux, macOS, etc.), or many .NET platforms (Desktop CLR, UWP) which run on a single operating system platform (Windows).
As time passed, the limitations of PCLs became apparent (a topic outside the scope of this document). The
key piece of information is that netstandard
is essentially an improvement upon portable
class libraries; and like PCLs, they represent API sets that support multiple platforms (for more information,
see https://docs.microsoft.com/en-us/dotnet/standard/net-standard).
Your unit tests always run on a platform. In our comparison, they are essentially applications, except that they rely on a shell "launcher" (what we call a Test Runner) to get them running. xUnit.net contains many test runners, which support a variety of platforms (some only support one platform, whereas others support many platforms).
In order to correctly build and run, your unit tests must target a specific platform, just like a console or GUI application does. And, just like a console or GUI application, if you want your tests to be able to run on several platforms, you should use multi-targeting to compile them against several platforms.
The requirement that your unit tests target a specific platform is not just an arbitrary rule; the build
system needs to know what your target platform is in order to make your code ultimately executable. When
you build a PCL or a netstandard
class library, the decision on how to make your code runnable
comes later, after it's linked into the final application (which must target a platform). Since you
don't link your unit tests directly into the runners, the build system needs to know what platform you're going
to run on in order to generate the correct behavior and artifacts.
For example, desktop CLR projects need to have their dependencies copied locally; .NET Core applications need to
get a .deps.json
file generated by the build system with the list and version of those dependencies,
so they can be loaded at runtime. Neither of these steps take place if you target PCL or netstandard
,
which makes your code unrunnable in a lot of different scenarios.
Our Getting started with xUnit.net (.NET Core / ASP.NET Core) documentation page includes instructions on supporting multi-targeting with your unit tests (showing how to support both .NET Core and Desktop CLR). Please review this documentation for more information.