如何基于当前平台跳过 xUnit 中的特定测试

  • 我在 Windows 上建了一个程序集
  • 我想在 Linux 的 mono 上运行 xUnit 测试。

然而,我发现虽然这些测试中有400个可以按顺序运行,但是某些测试要么挂起 xUnit 运行程序,要么完全关闭它。

如果某些测试不能在 Linux 上运行,我不会使用 关心,因为某些测试与 DTC 和一些我们不需要支持的非托管 gumph 有关。

但是,我确实希望忽略这些测试,并且在构建输出中正确地标记忽略的测试。

这个问题可以归结为我猜想的一些可能的解决办法

  • 如何通过控制台运行程序在 xUnit 中运行特定的测试?(我还没有找到这方面的文档,也许我只是不够努力)
  • 有没有可能反过来说“这是一个程序集,请忽略这些特定的测试”
  • 有人建议,在这些测试中加入属性是一种更好的方式,可以正式地记录这些测试是特定于平台的——这可能吗?

如果我能够避免对原始代码进行过多的修改,那就太好了,因为这些代码实际上不是我可以修改的,而且应用大量的跨平台技巧可能不会太顺利。

111278 次浏览

我会避免外部化跳过测试(例如,如果可能的话,一个配置/命令文件)。这在某种程度上有悖于使测试易于运行和值得信赖。当其他人开始参与时,在代码中忽略测试是最安全的方法。

我可以看到许多选项,这里有两个涉及修改现有代码的选项。

选项1-最具侵入性,编译时平台检测

在 VS 解决方案中,定义另一个定义预编译器标志 MONOWIN的配置(这样它就是一个明确的标志,说明它是在 Windows 上编译的代码,用于 Mono)。

然后定义一个属性,使测试在为 Mono 编译时被忽略:

public class IgnoreOnMonoFactAttribute : FactAttribute {
#if MONOWIN
public IgnoreOnMonoFactAttribute() {
Skip = "Ignored on Mono";
}
#endif
}

实际上,很难找到这种方法的任何优点,因为它包含了对原始解决方案的模拟,并添加了另一个需要支持的确认。

选项2-有点侵入性-运行时平台检测

这里有一个与 option1类似的解决方案,只是不需要单独的配置:

public class IgnoreOnMonoFactAttribute : FactAttribute {


public IgnoreOnMonoFactAttribute() {
if(IsRunningOnMono()) {
Skip = "Ignored on Mono";
}
}
/// <summary>
/// Determine if runtime is Mono.
/// Taken from http://stackoverflow.com/questions/721161
/// </summary>
/// <returns>True if being executed in Mono, false otherwise.</returns>
public static bool IsRunningOnMono() {
return Type.GetType("Mono.Runtime") != null;
}
}

注1

如果一个方法被标记为 [Fact][IgnoreOnMonoFact],xUnit 运行程序将运行两次。(CodeRush 不这样做,在本例中我假设 xUnit 是正确的)。这意味着任何测试方法都必须将 [Fact]替换为 [IgnoreOnMonoFact]

注2

CodeRush 测试运行程序仍然运行 [IgnoreOnMonoFact]测试,但是它忽略了 [Fact(Skip="reason")]测试。我假设这是由于 CodeRush 反映了 xUnit,而不是在 xUnit 库的帮助下实际运行它。这在 xUnit 运行器中可以很好地工作。

这将是 trait 的理想用法,但不幸的是,命令行和 xml 项目文件都不支持基于 trait 的过滤。为此,值得在 codebx 站点中添加一个问题。

这个问题现在已经在1.8中解决了-你可以在 Traits 上进行过滤。

更新: Traits 适用于控制台运行器,但不适用于 MSBuild,我已经为此支持添加了一个特性请求。

现在有了新的选择。

添加 Nuget 包 跳过事实,它允许您使用 [SkippableFact]而不是 [Fact],并且您可以在测试中使用 Skip.<xyz>在运行时动态跳过测试。

例如:

[SkippableFact]
public void SomeTestForWindowsOnly()
{
Skip.IfNot(Environment.IsWindows);


// Test Windows only functionality.
}

XUnit v2.0现在可用了,它直接支持可跳过的测试。使用:

[Fact (Skip = "specific reason")]

[Fact(Skip="reason")]

但我更喜欢使用 trait

[Fact, Trait("type","unit")]
public void MyUnitTest(){
// given
// when
// then
}


[Fact, Trait("type","http")]
public void MyHttpIntegrationTest(){
// given
// when do things over HTTP
// then
}

用途

dotnet test --filter type=unit

这可以保护我们的构建不会意外地运行开发人员忘记跳过的集成测试,例如 [Fact(Skip="Integration")],但是它确实需要单元测试通过添加正确的特性来“选择”到 CI,这些特性当然不是很好。

多米尼克的解决方案是这样的:

[SkippableFact]
public void Get_WhenCall_ReturnsString()
{
// Arrange
Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));


// Act


// Assert


}

为了补充以前关于 SkippableFact 的答案: 请注意,每个测试仍在构造中——构造函数正在运行。

如果您在基类构造函数中使用了耗时的代码,那么另一种方法是在合适的文件中收集特定于环境的测试用例,并在构造函数中运行环境检查:

        if (!SupportsTemporalQueries())
throw new SkipException("This test class only runs in environments support temporal queries");

这可以大大加快测试运行的速度。在我们的系统中,我们要么扩展一个“通用”基础测试类(在所有环境中运行) ,要么扩展一个特定于环境的基础测试类。我发现这比在管道或其他解决方案中进行过滤更容易维护。