【问题标题】:How to run singleton method only once in multiple test projects如何在多个测试项目中只运行一次单例方法
【发布时间】:2020-12-04 12:31:13
【问题描述】:

我有三个测试项目。所有这些都引用自 Model 项目:

我在 Model 项目中创建了 TestBase 类,并将其用作所有测试类的基类。 继承看起来像这样:

[TestClass]
    public class UnitTest1 : TestBase
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }

在 TestBase 我有 TestInitialize:

 [TestInitialize]
        public void Initialize()
        {
            SingletonClass.Initiate();
        }

        [TestCleanup]
        public void Cleanup()
        {
            //Do some
        }

SingletonClass 是测试中需要的静态类。我需要启动一次,所以我这样做:

public static class SingletonClass
    {
        public static bool IsInitiated { get; set; }

        public static void Initiate()
        {
            if (!IsInitiated)
            {
                IsInitiated = true;
                //Do some
                Thread.Sleep(5000);
            }
        }
    }

当我运行一个项目的测试时它工作正常,但是当我运行所有它时它启动它 3 次(花了 15 秒)。当我运行所有测试时,有没有办法只运行一次?

【问题讨论】:

  • 这在很大程度上取决于您的测试运行器实现。运行者可能会为测试项目创建不同的进程,因此每个进程都会进行一次初始化。您可以通过将--agents=1 应用到您对 nunit 的调用来限制进程数。
  • 这应该会为你解决stackoverflow.com/questions/1873191/…

标签: c# unit-testing mstest


【解决方案1】:

根据您的测试运行者,您可能会为每个测试项目获得一个新流程。因此单例确实被实例化了三次,每个进程一次。

你可以通过告诉 nunit 对所有项目只使用一个进程来解决这个问题:

 nunit3-console.exe MyProject.dll --agents=1

编辑:对于 vstest,您可以设置MaxCpuCount-参数。详情请参阅docs

<MaxCpuCount>1</MaxCpuCount>

【讨论】:

  • 我正在使用 MSTest 包。如何在 MSTest 中做到这一点? vstest.console.exe 没有这个参数
  • 它不工作。 MaxCpuCount 正在为每个项目计算。默认为1
【解决方案2】:

最后,我找到了解决方案。我使用 ILRepack 将所有项目合并到一个新项目(AllTests)中:

 <!-- ILRepack -->
  <Target Name="ILRepack" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug'">
    <PropertyGroup>
      <WorkingDirectory>$(OutputPath)</WorkingDirectory>
    </PropertyGroup>
    <ItemGroup>
      <InputAssemblies Include="$(OutputPath)MainTest.dll" />
      <InputAssemblies Include="$(OutputPath)FIrst.dll" />
      <InputAssemblies Include="$(OutputPath)Second.dll" />
      <InputAssemblies Include="$(OutputPath)Third.dll" />
    </ItemGroup>
    <Message Text="MERGING: @(InputAssemblies->'%(Filename)') into $(OutputAssembly)" Importance="High" />
    <ILRepack OutputType="$(OutputType)"  Internalize="false" MainAssembly="$(AssemblyName).dll" OutputAssembly="$(AssemblyName).dll" InputAssemblies="@(InputAssemblies)" WorkingDirectory="$(WorkingDirectory)" />
  </Target>
  <!-- /ILRepack -->

现在,我可以使用 vstest.console.exe 运行所有测试:

vstest.console.exe AllTests.dll

或者我可以从 Visual Studio 运行它:

单例类只准时启动。我不知道它如何产生负面影响,但目前,它运行良好

【讨论】:

    【解决方案3】:

    您在 3 个并行消费者之间存在共享状态问题。您无法控制 vstests 如何创建像 Ben 解释的流程。

    这样做的唯一方法是将 SingletonClass 初始化移动到它自己的进程中,并让其他 3 个测试项目与该单独的进程通信。这将确保无论 vstest 创建多少进程,它只发生一次。

    当然,只有在初始化非常昂贵的情况下才值得这样做。

    通常您应该避免在测试之间共享状态。这会使它们变脆。

    【讨论】:

      【解决方案4】:

      恕我直言,应根据测试定义使用的资源。这样,以前的测试就不会搞砸当前的测试。但是,如果您希望这种情况发生,您可以使用单例模式。喜欢:

      public class MyClass
      {
          private static readonly MyClass _myClass = new MyClass();
      
          private MyClass()
          {
              // Initialize your class here
          }
      
          public static MyClass Instance
          {
              get => _myClass;
          }
      }
      

      静态值应该在多个单元测试中保持活跃。

      【讨论】:

        【解决方案5】:

        类可以有一个构造函数,该构造函数仅在第一次引用该类时才被调用。

        public static class SingletonClass
            {
                static SingletonClass()
                {
                     IsInitiated = true;
                     //Do some
                     Thread.Sleep(5000);
                }
            }
        

        【讨论】:

        • 我没有,我只是在将来添加 incase 他在通用类上使用静态构造函数。因此单词 if。
        • 从我提供的代码中可以看出,它不是泛型类。
        • 如果他粘贴我上面提供的代码,它会解决问题。
        • 我已经删除了额外的信息,以方便您使用。
        • 是的,但是静态构造函数并没有比 OP 的初始化方法带来 任何 好处,因为问题只是与测试运行器产生多个进程有关。因此,您的静态构造函数 多次调用 - 每个进程一次。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-01-19
        • 1970-01-01
        • 1970-01-01
        • 2013-10-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多