159

我正在尝试对我编写的 WCF 主机管理引擎进行单元测试。该引擎基本上根据配置动态创建 ServiceHost 实例。这使我们能够动态地重新配置哪些服务可用,而不必在添加新服务或删除旧服务时将所有服务关闭并重新启动它们。

但是,由于 ServiceHost 的工作方式,我在对该主机管理引擎进行单元测试时遇到了困难。如果已经为特定端点创建、打开并且尚未关闭 ServiceHost,则无法为同一端点创建另一个 ServiceHost,从而导致异常。由于现代单元测试平台并行执行测试,我没有有效的方法对这段代码进行单元测试。

我使用过 xUnit.NET,希望由于它的可扩展性,我可以找到一种方法来强制它连续运行测试。但是,我没有任何运气。我希望 SO 上的某个人遇到过类似的问题,并且知道如何让单元测试连续运行。

注意:ServiceHost是一个 WCF 类,由 Microsoft 编写。我没有能力改变它的行为。只托管每个服务端点一次也是正确的行为......但是,它并不是特别有利于单元测试。

4

11 回答 11

230

每个测试类都是一个独特的测试集合,它下的测试将按顺序运行,因此如果您将所有测试放在同一个集合中,那么它将按顺序运行。

在 xUnit 中,您可以进行以下更改以实现此目的:

以下将并行运行:

namespace IntegrationTests
{
    public class Class1
    {
        [Fact]
        public void Test1()
        {
            Console.WriteLine("Test1 called");
        }

        [Fact]
        public void Test2()
        {
            Console.WriteLine("Test2 called");
        }
    }

    public class Class2
    {
        [Fact]
        public void Test3()
        {
            Console.WriteLine("Test3 called");
        }

        [Fact]
        public void Test4()
        {
            Console.WriteLine("Test4 called");
        }
    }
}

要使其连续,您只需将两个测试类放在同一个集合下:

namespace IntegrationTests
{
    [Collection("Sequential")]
    public class Class1
    {
        [Fact]
        public void Test1()
        {
            Console.WriteLine("Test1 called");
        }

        [Fact]
        public void Test2()
        {
            Console.WriteLine("Test2 called");
        }
    }

    [Collection("Sequential")]
    public class Class2
    {
        [Fact]
        public void Test3()
        {
            Console.WriteLine("Test3 called");
        }

        [Fact]
        public void Test4()
        {
            Console.WriteLine("Test4 called");
        }
    }
}

有关更多信息,您可以参考此链接

于 2017-11-08T08:30:54.927 回答
127

重要提示:此答案适用于 .NET Framework。对于 dotnet 核心,请参阅 Dimitry 关于xunit.runner.json.

所有好的单元测试都应该 100% 隔离。使用共享状态(例如,取决于static每个测试修改的属性)被认为是不好的做法。

话虽如此,您关于按顺序运行 xUnit 测试的问题确实有答案!我遇到了完全相同的问题,因为我的系统使用了静态服务定位器(不太理想)。

默认情况下,xUnit 2.x 并行运行所有测试。这可以通过CollectionBehavior在您的测试项目中的 AssemblyInfo.cs 中定义来修改每个程序集。

对于每个组件的分离使用:

using Xunit;
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]

或者根本不使用并行化:

[assembly: CollectionBehavior(DisableTestParallelization = true)]

后者可能是您想要的。有关并行化和配置的更多信息,请参见xUnit 文档

于 2016-01-19T12:34:16.800 回答
110

对于 .NET Core 项目,使用以下命令创建xunit.runner.json

{
  "parallelizeAssembly": false,
  "parallelizeTestCollections": false
}

此外,您csproj应该包含

<ItemGroup>
  <None Update="xunit.runner.json"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

对于旧的 .Net Core 项目,您project.json应该包含

"buildOptions": {
  "copyToOutput": {
    "include": [ "xunit.runner.json" ]
  }
}
于 2016-07-24T23:53:17.060 回答
26

xunit.runner.json对于 .NET Core 项目,您可以使用文件配置 xUnit ,如https://xunit.net/docs/configuration-files中所述。

您需要更改以停止并行测试执行的设置是parallelizeTestCollections,默认为true

true如果程序集愿意在此程序集中彼此并行运行测试,请将其设置为。... 将此设置false为禁用此测试程序集中的所有并行化。

JSON 模式类型:布尔
默认值:true

xunit.runner.json因此,为此目的的最小值看起来像

{
    "parallelizeTestCollections": false
}

如文档中所述,请记住通过以下方式将此文件包含在您的构建中:

  • 如果Visual Studio中文件的属性较新,则将“复制到输出目录”设置为“复制”,或

  • 添加

      <Content Include=".\xunit.runner.json">
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      </Content>
    

    到您的.csproj文件,或

  • 添加

      "buildOptions": {
        "copyToOutput": {
          "include": [ "xunit.runner.json" ]
        }
      }
    

    到你的project.json文件

取决于您的项目类型。

最后,上述之外,如果您使用的是 Visual Studio,请确保您没有意外单击Run Tests In Parallel按钮,这将导致测试并行运行,即使您在xunit.runner.json. 微软的 UI 设计人员巧妙地将这个按钮设置为无标签,难以注意到,并且距离测试资源管理器中的“全部运行”按钮大约一厘米,只是为了最大限度地提高你误击它并且不知道为什么你的测试的机会突然失败:

带有圆圈按钮的屏幕截图

于 2017-07-10T22:39:04.757 回答
18

这是一个老问题,但我想为像我这样新搜索的人写一个解决方案:)

注意:我在使用 xunit 版本 2.4.1 的 Dot Net Core WebUI 集成测试中使用此方法。

创建一个名为 NonParallelCollectionDefinitionClass 的空类,然后将 CollectionDefinition 属性赋予该类,如下所示。(重要的部分是 DisableParallelization = true 设置。)

using Xunit;

namespace WebUI.IntegrationTests.Common
{
    [CollectionDefinition("Non-Parallel Collection", DisableParallelization = true)]
    public class NonParallelCollectionDefinitionClass
    {
    }
}

然后将 Collection 属性添加到您不希望它并行运行的类中,如下所示。(重要的部分是集合的名称。它必须与 CollectionDefinition 中使用的名称相同)

namespace WebUI.IntegrationTests.Controllers.Users
{
    [Collection("Non-Parallel Collection")]
    public class ChangePassword : IClassFixture<CustomWebApplicationFactory<Startup>>
    ...

当我们这样做时,首先会运行其他并行测试。之后,运行具有 Collection("Non-Parallel Collection") 属性的其他测试。

于 2019-12-01T10:51:11.287 回答
6

我不知道细节,但听起来您可能正在尝试进行集成测试而不是单元测试。如果您可以隔离对 的依赖ServiceHost,那可能会使您的测试更容易(更快)。因此(例如)您可以独立测试以下内容:

  • 配置阅读类
  • ServiceHost 工厂(可能作为集成测试)
  • IServiceHostFactory接受一个和一个的引擎类IConfiguration

有帮助的工具包括隔离(模拟)框架和(可选)IoC 容器框架。看:

于 2009-09-11T01:22:11.983 回答
6

你可以使用播放列表

右键单击测试方法 -> 添加到播放列表 -> 新建播放列表

然后你可以指定执行顺序,默认是,当你将它们添加到播放列表时,你可以根据需要更改播放列表文件

在此处输入图像描述

于 2015-03-13T18:21:05.500 回答
4

也许您可以使用高级单元测试它允许您定义运行测试的顺序。因此,您可能必须创建一个新的 cs 文件来托管这些测试。

以下是您可以如何改变测试方法以按照您想要的顺序工作。

[Test]
[Sequence(16)]
[Requires("POConstructor")]
[Requires("WorkOrderConstructor")]
public void ClosePO()
{
  po.Close();

  // one charge slip should be added to both work orders

  Assertion.Assert(wo1.ChargeSlipCount==1,
    "First work order: ChargeSlipCount not 1.");
  Assertion.Assert(wo2.ChargeSlipCount==1,
    "Second work order: ChargeSlipCount not 1.");
  ...
}

请让我知道它是否有效。

于 2009-09-11T09:29:52.613 回答
1

对我来说,在 .Net Core Console 应用程序中,当我想同步运行测试方法(而不是类)时,唯一有效的解决方案是这个博客中描述的: xUnit:控制测试执行顺序

于 2020-04-07T10:39:34.480 回答
1

到目前为止,没有一个建议的答案对我有用。我有一个带有 XUnit 2.4.1 的 dotnet 核心应用程序。我通过在每个单元测试中放置一个锁来实现所需的行为。就我而言,我不关心运行顺序,只是测试是顺序的。

public class TestClass
{
    [Fact]
    void Test1()
    {
        lock (this)
        {
            //Test Code
        }
    }

    [Fact]
    void Test2()
    {
        lock (this)
        {
            //Test Code
        }
    }
}
于 2020-02-06T13:02:45.590 回答
0

我在基类中添加了属性[Collection("Sequential")]

    namespace IntegrationTests
    {
      [Collection("Sequential")]
      public class SequentialTest : IDisposable
      ...


      public class TestClass1 : SequentialTest
      {
      ...
      }

      public class TestClass2 : SequentialTest
      {
      ...
      }
    }
于 2019-04-05T23:18:52.347 回答