如何在多个目标框架上适当地单元测试.NET项目,考虑到各个目标之间的实现差异?

48
考虑一个.NET类库,其针对以下框架进行目标设置:
  • .NET Framework 2.0
  • .NET Framework 4.6
  • .NET Standard 1.0
  • .NET Standard 1.3
  • .NET Portable Profile336
让我们暂时不用担心为什么这是目标框架的确切列表。
假设所有目标框架下的外部API都相同,但每个单独的目标框架都有一些特定于该框架的代码,那么我应该如何编写正确的单元测试以覆盖所有目标框架的代码?
我正在运行Visual Studio 2017 Community。
我在GitHub上准备了一个最小化的示例https://github.com/airbreather/MultiTargetDemo,这是我目前发现的可能是我现在可以做到的最好的,但这似乎不是正确的方法。一些缺陷:
  • 每个目标框架都需要单独的.csproj文件,需要大量复制粘贴。
  • 使用一个名为ReferringTargetFrameworkForProjectReferences的未经充分记录的属性。
    • *这有点低调...目前在谷歌上搜索它,除了Microsoft的SDK材料外,什么也没有。
  • 似乎不能正确地与.NET Standard shims一起使用(.NET Standard 1.3在此处抛出运行时错误,尝试查找System.IO.FileSystem。(编辑:我已经弄清楚了,这是corefx issue #16322,有一个笨拙的解决方法。)
上面链接的库有一个公共类,只有一个公共实例方法。根据合同,该方法只需返回32位整数值3,但它在每个目标上以不同的方式计算出3。显然,实际用例围绕实现更加“有趣”的方法,但这已足够进行对话。
此外,请注意,该库中的测试类实际上使用了一些在所有目标平台上都不可用的内容:System.Threading.Tasks.Task<TResult>在.NET Framework 2.0上不可用...这充当了测试本身可能用于自己目的的任何内容的代理,这些内容可能在正在测试的目标框架中不一定可用。
实际用例是NetTopologySuite,它已经针对多个完整框架平台和一些PCL进行了优化;有一个一年前的问题要求使其与.NET Core一起工作。我一直在断断续续地努力尝试让它在一个月左右的时间内实现,现在终于遇到了这个问题。
我发现另一个问题:使用.NET Core和xUnit同时针对多个框架进行单元测试代码,这确实与此问题相似。但是有几点不同:
  • 那个问题使用了VS2015时代的预览SDK工具(project.json)。这个问题涉及到VS2017时代的非预览工具,具有漂亮的.csproj文件等。
  • 可以通过为测试项目本身多目标定位一个“测试”目标框架来解决该问题,每个目标框架都针对“被测试对象”所针对的框架。看起来这个问题无法以类似的方式解决,特别是针对不寻常的Profile336目标。
    • 此外,鉴于完整的.NET Framework 4.6可以引用任何列出的五个目标框架之一的程序集,因此我不需要将基本测试类的实现限制在仅可用于所选抓取的任何目标框架的交集中。
    • 我尝试过像在TargetFrameworks中列出合成内容,例如test1; test2; test3; test4; test5,然后强制将TargetFrameworkIdentifier + TargetFrameworkVersion设置为.NETFramework + 4.6,但我没有取得太大的成功(尝试获取xunit包时失败)。但是我对此也没有尝试过努力。

1
您需要为每个“目标”平台构建测试项目,如.NET Framework 2.0/4.x等。维护这么多测试项目可能会很痛苦,但这是我能想到的唯一方法。 - Lex Li
我已在GitHub上提交了一个请求,希望添加类似于我所寻找的功能的支持:https://github.com/Microsoft/vstest/issues/1013 - Joe Amenta
2个回答

55

首先,测试项目可以使用<TargetFrameworks>属性进行多目标设置,就像你为库所做的那样。虽然VS测试运行器目前仅显示/运行一个框架(特指第一个),但任何对dotnet test的调用都将执行所有框架(xunit也正在开发自定义控制台运行器 - dotnet xunit - 以获得更好的控制台UX)。

此外,请注意测试项目需要指定具有运行时的目标框架。因此,您的测试项目可以以netcoreapp*net*为目标,但从未以netstandard*为目标,因为它没有关联的运行时来运行测试。我不确定可移植目标,因为每个测试项目都需要引用Microsoft.NET.Tests.Sdk才能获得测试运行器支持,而这些条件并不适用于NETPortable。

因此,您需要选择要测试/支持的所有netnetcoreapp版本 - 例如net20;net45;net46;netcoreapp1.0将涵盖您的所有 .net框架和.net core版本。

有一种方法可以通过在<ProjectReference>元素上设置Properties="TargetFramework=netstandard1.3"来强制依赖于特定的目标框架,但这可能很难在还原和多目标设置方面正常工作。


是的,我对测试项目本身的多目标化存在问题,因为虽然主要项目偶尔必须削弱自己,但我不想对测试项目做同样的事情...第二个问题是,正如你所提到的,VS测试运行器目前无法运行超出第一个的测试,因此这是一种花费更多精力以获得更糟结果的练习。Properties="TargetFramework=netstandard1.3"听起来很不错,作为ReferringTargetFrameworkForProjectReferences的更精确的替代方案。 - Joe Amenta
使用包引用格式,您可以执行以下操作: - John

2

刚遇到了这个问题。 结果发现,由于不能使用 .net standard 进行测试,我必须在测试项目中针对特定的框架版本进行目标设置,如 .net5、4.6.2 等等。 然后,在运行测试时,它们将运行针对这些版本的测试,就像使用针对其他版本进行目标设置的库一样。

我们的测试覆盖率显示 .net5 为 94%,而其他版本则为 0%,直到我们做到这一点。

enter image description here


1
谢谢。您使用的是哪个VS扩展程序来查看报告? - Joy George Kunjikkuru
2
嗨,这不是一个扩展,而是'Rider' IDE中的代码覆盖率报告。 - Aaron Gibson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接