鉴于目标之间的实现差异,如何正确地对具有多个目标框架的 .NET 项目进行单元测试? [英] How to properly unit test a .NET project with multiple target frameworks, given implementation differences among targets?

查看:17
本文介绍了鉴于目标之间的实现差异,如何正确地对具有多个目标框架的 .NET 项目进行单元测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑一个面向以下框架的 .NET 类库:

  • .NET 框架 2.0
  • .NET 框架 4.6
  • .NET 标准 1.0
  • .NET 标准 1.3
  • .NET 便携式配置文件336

我们不要立即担心为什么这是目标框架的确切列表.

假设跨所有目标框架的外部 API 是相同的,但是每个单独的目标框架都有一些该框架独有的代码,我如何正确编写涵盖所有代码的单元测试目标框架?

我正在运行 Visual Studio 2017 社区.

我在 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 在 上不可用.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 包时失败了).不过,我也没有在这方面努力.

解决方案

首先,测试项目也可以多目标,利用属性就像您用于图书馆一样.虽然 VS 测试运行器目前只显示/运行一个框架(第一个特定的框架),任何 dotnet test 的调用都将执行所有框架(xunit 也在开发自定义控制台运行器 - dotnet xunit - 以获得更好的控制台用户体验).

还要记住,测试项目需要指定一个具有运行时的目标框架.因此,您的测试项目可以针对 netcoreapp*net*,但不能针对 netstandard*,因为它没有关联的运行时来运行测试.我不确定可移植目标,因为曾经测试项目需要引用以获得测试运行器支持的 Microsoft.NET.Tests.Sdk 没有 NETPortable 的条件.

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

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

Consider a .NET class library that targets the following frameworks:

  • .NET Framework 2.0
  • .NET Framework 4.6
  • .NET Standard 1.0
  • .NET Standard 1.3
  • .NET Portable Profile336

Let's not immediately worry about why this is the exact list of target frameworks.

Supposing that the external API across all target frameworks is identical, but 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?

I'm running Visual Studio 2017 Community.

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: https://github.com/airbreather/MultiTargetDemo, but this doesn't feel like the right way to do it. Some flaws:

  • Requires a separate .csproj file for each target framework with lots of copy-paste.
  • Uses a not-well-documented* property called ReferringTargetFrameworkForProjectReferences.
    • *that's a bit of an understatement... Googling it at the moment, literally nothing shows up apart from Microsoft's SDK stuff.
  • Doesn't seem to work properly with the .NET Standard shims (.NET Standard 1.3 here throws at runtime trying to look for System.IO.FileSystem). (edit: figured it out, this was corefx issue #16322, which has a bit of an awkward workaround).

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.

Furthermore, note that the test class in that library actually uses some stuff on its own that's not available on all target platforms: System.Threading.Tasks.Task<TResult> 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.

The actual use case is NetTopologySuite, which already targets multiple full-framework platforms and some PCLs; there's a year-old issue 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.

I found that other question: Unit testing code targeting several frameworks at once with .NET Core and xUnit, which is indeed similar to this one. This is different for a few reasons:

  • That question was using the VS2015-era preview SDK tooling (project.json). This question refers to the VS2017-era non-preview tooling with the pretty .csproj files and all.
  • 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.
    • 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.
    • I did try a hack like listing synthetic stuff in TargetFrameworks like test1;test2;test3;test4;test5 and then forcing TargetFrameworkIdentifier + TargetFrameworkVersion to .NETFramework + 4.6, but I didn't have much success (it failed when trying to grab the xunit package). I also didn't try too hard on this, however.

解决方案

First, test projects can multi-target as well by making use of the <TargetFrameworks>property just like you used for the library. While the VS test runner currently only shows/runs one framework (the first one to be specific), any invocation of dotnet test will execute all frameworks (xunit is also developing custom console runner - dotnet xunit - for a better console UX).

Also keep in mind that test projects need to specify a target framework that has a runtime. So your test projects can either target netcoreapp* or net* but never netstandard* since it does not have a runtime associated to run the tests. I'm not sure about the portable target since the Microsoft.NET.Tests.Sdk that ever test project needs to reference to get test runner support doesn't have conditions for NETPortable.

So you'd need to pick all the versions of net and netcoreapp that you need to test on / support - e.g. net20;net45;net46;netcoreapp1.0 would cover all your .net framework and .net core versions.

There is a way to force a dependency to a specific target framework by setting a Properties="TargetFramework=netstandard1.3" on the <ProjectReference> element, but this may be hard to get working correctly for restore and multi-targeting.

这篇关于鉴于目标之间的实现差异,如何正确地对具有多个目标框架的 .NET 项目进行单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆