title: How to properly unit test a .NET project with multiple target frameworks, given implementation differences among targets?
body: <p>Consider a .NET class library that targets the following frameworks:</p>
  
  <ul>
  <li>.NET Framework 2.0</li>
  <li>.NET Framework 4.6</li>
  <li>.NET Standard 1.0</li>
  <li>.NET Standard 1.3</li>
  <li>.NET Portable Profile336</li>
  </ul>
  
  <p>Let's not immediately worry about why this is the exact list of target frameworks.</p>
  
  <p>Supposing that the external API across all target frameworks is identical, <strong>but</strong> each individual target framework has some code that's unique to that framework, how do I properly write the unit tests that cover the code from all target frameworks?</p>
  
  <p>I'm running Visual Studio 2017 Community.</p>
  
  <p>I've cooked up a ready-to-go minimal sample on GitHub here with what I've found so far to be probably the best I can do right now: <a href="https://github.com/airbreather/MultiTargetDemo" rel="noreferrer">https://github.com/airbreather/MultiTargetDemo</a>, but this doesn't feel like the right way to do it.  Some flaws:</p>
  
  <ul>
  <li>Requires a separate .csproj file for each target framework with lots of copy-paste.</li>
  <li>Uses a not-well-documented* property called <code>ReferringTargetFrameworkForProjectReferences</code>.
  
  <ul>
  <li>*that's a bit of an understatement... Googling it at the moment, literally nothing shows up apart from Microsoft's SDK stuff.</li>
  </ul></li>
  <li><s>Doesn't seem to work properly with the .NET Standard shims (.NET Standard 1.3 here throws at runtime trying to look for <code>System.IO.FileSystem</code>)</s>.  (edit: figured it out, this was <a href="https://github.com/dotnet/corefx/issues/16322" rel="noreferrer">corefx issue #16322</a>, which has a bit of an <a href="https://github.com/Microsoft/msbuild/issues/1310#issuecomment-300155346" rel="noreferrer">awkward workaround</a>).</li>
  </ul>
  
  <p>The library in the above-linked has precisely one public class with precisely one public instance method on it.  The method is contractually obligated to just return the 32-bit integer value 3, but it computes that 3 a materially different way on each target.  Obviously the actual use case revolves around implementing much more "interesting" methods, but this is sufficient for conversation.</p>
  
  <p>Furthermore, note that the test class in that library actually uses some stuff on its own that's not available on all target platforms: <code>System.Threading.Tasks.Task&lt;TResult&gt;</code> is not available on .NET Framework 2.0... this serves as a proxy for just anything that the test itself might be using for its own purposes, which might not necessarily be available in the target framework that's being tested.</p>
  
  <p>The actual use case is <a href="https://github.com/NetTopologySuite/NetTopologySuite" rel="noreferrer">NetTopologySuite</a>, which already targets multiple full-framework platforms and some PCLs; there's a <a href="https://github.com/NetTopologySuite/NetTopologySuite/issues/110" rel="noreferrer">year-old issue</a> asking to make it work with .NET Core.  I've been working on-and-off to try to make that happen for just about a month now, and I'm finally stumbling into this issue.</p>
  
  <p>I found that other question: <a href="https://stackoverflow.com/q/38708034/1083771">Unit testing code targeting several frameworks at once with .NET Core and xUnit</a>, which is indeed similar to this one.  This is different for a few reasons:</p>
  
  <ul>
  <li>That question was using the VS2015-era preview SDK tooling (<code>project.json</code>).  This question refers to the VS2017-era non-preview tooling with the pretty <code>.csproj</code> files and all.</li>
  <li>That question could be resolved by simply multi-targeting the test project itself with one "test" target framework per framework targeted by the "subject-under-test".  It does not appear that this question can be resolved in a similar way, especially with the unusual Profile336 target.
  
  <ul>
  <li>Besides, given that the full .NET Framework 4.6 can reference assemblies targeted at any of the listed five target frameworks, there should be no need for me to have to limit the implementations of the fundamental test classes to just what's available in the intersection of whatever grab-bag of target frameworks that get selected for this.</li>
  <li>I did try a hack like listing synthetic stuff in <code>TargetFrameworks</code> like <code>test1;test2;test3;test4;test5</code> and then forcing <code>TargetFrameworkIdentifier</code> + <code>TargetFrameworkVersion</code> to .NETFramework + 4.6, but I didn't have much success (it failed when trying to grab the <code>xunit</code> package).  I also didn't try <strong>too</strong> hard on this, however.</li>
  </ul></li>
  </ul>