简体   繁体   中英

How to run singleton method only once in multiple test projects

I have three test projects. All of them referenced from the Model project:

项目

I created the TestBase class in the Model project and using it as the base class of all test classes. Inheritance looks like this:

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

In TestBase I have TestInitialize:

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

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

SingletonClass is a static class that needs in tests. I need to initiate it once so I am doing this:

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

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

It is working fine when I run tests of one project, but when I run all it is initiating it 3 times(took 15 seconds). Is there any way to run it only once when I run all tests?

Depending on your test-runner you may get a new process for every test-project. Therefor the singleton is indeed instantiated three times, once per process.

You can solve that by telling nunit to only use a single process for all projects:

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

EDIT: For vstest you can set the MaxCpuCount -parameter. See the docs for further details:

<MaxCpuCount>1</MaxCpuCount>

Finally, I found a solution. I merged all projects into a new project (AllTests) using ILRepack:

 <!-- 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 -->

Now, I can run all tests using vstest.console.exe:

vstest.console.exe AllTests.dll

Or I can run it from the visual studio:

从 vs 运行测试

Singleton class initiating only on time. I don't know how it affects negatively, but currently, it is working fine

You have a shared state problem between 3 parallel consumers. You don't control how vstests creates processes like Ben explained.

The only way to do this is to move the SingletonClass initialization in its own process and have the other 3 Test projects communicate with that separate process. That will make sure it only happens once no matter how many processes are created by vstest.

Of course, that's only worth doing if that initialization is really expensive.

Generally you should avoid shared state between Tests. It makes them brittle.

Imho used resources should be defined per test. That way a previous test can't botch up the current test. But if you want this to happen, you can make use of the Singleton pattern. Like:

public class MyClass
{
    private static readonly MyClass _myClass = new MyClass();

    private MyClass()
    {
        // Initialize your class here
    }

    public static MyClass Instance
    {
        get => _myClass;
    }
}

Static values should be kept alive over multiple unit tests.

Classes have the ability to have a constructor that is called only when the class is first referenced.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM