连续执行单元测试(而不是并行执行)

问题描述 投票:55回答:8

我正在尝试对我编写的WCF主机管理引擎进行单元测试。该引擎基本上基于配置动态创建ServiceHost实例。这允许我们动态地重新配置哪些服务可用,而无需在添加新服务或删除旧服务时将其全部关闭并重新启动它们。

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

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

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

c# .net unit-testing xunit.net
8个回答
75
投票

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

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

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

对于每个组件分离使用:

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

或者根本没有并行化:

[assembly: CollectionBehavior(DisableTestParallelization = true)]

后者可能就是你想要的那个。有关并行化和配置的更多信息可以在xUnit documentation上找到。


55
投票

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

在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");
        }
    }
}

有关更多信息,请参阅this link


44
投票

对于.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" ]
  }
}

15
投票

对于.NET Core项目,您可以使用xunit.runner.json文件配置xUnit,如https://xunit.github.io/docs/configuring-with-json.html所述。

您需要更改以停止并行测试执行的设置是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,请确保您没有意外地单击“并行运行测试”按钮,这将导致测试并行运行,即使您已关闭xunit.runner.json中的并行化。微软的UI设计人员狡猾地将这个按钮取消标记,很难注意到,并且距离测试资源管理器中的“全部运行”按钮大约一厘米,只是为了最大限度地提高您误操作的机会,并且不知道为什么要进行测试突然失败了:

Screenshot with the button circled


6
投票

你可以使用播放列表

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

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


5
投票

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

  • 配置阅读课
  • ServiceHost工厂(可能作为集成测试)
  • 采用IServiceHostFactoryIConfiguration的引擎类

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


3
投票

也许你可以使用Advanced Unit TestingIt allows you to define the sequence in which you run the test。因此,您可能必须创建一个新的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.");
  ...
}

请告诉我它是否有效。


-2
投票

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

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


      public class TestClass1 : SequentialTest
      {
      ...
      }

      public class TestClass2 : SequentialTest
      {
      ...
      }
    }
© www.soinside.com 2019 - 2024. All rights reserved.