1

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

projects

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?

Dilshod K
  • 2,924
  • 1
  • 13
  • 46
  • that highly depends on your test-runner implementation. Chances are the runner creates different processes for ever test-project and thus initialization happens once per process. You may limit the number of processes by appliying `--agents=1` to your call to nunit. – MakePeaceGreatAgain Dec 04 '20 at 12:37
  • This should solve it for you https://stackoverflow.com/questions/1873191/why-does-testinitialize-get-fired-for-every-test-in-my-visual-studio-unit-tests – Ben Dec 04 '20 at 12:55

5 Answers5

1

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>
MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
1

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:

run test from vs

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

Dilshod K
  • 2,924
  • 1
  • 13
  • 46
0

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.

0

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.

mvesign
  • 41
  • 5
-1

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);
        }
    }
Ben
  • 757
  • 4
  • 14
  • I didn't, i was just adding incase in future he uses a static constructor on a genric class.Hence the word if. – Ben Dec 04 '20 at 12:41
  • As you can see from the code I provided it is not a generic class. – Ben Dec 04 '20 at 12:42
  • It does if he pastes in my code provided above it will solve the issue. – Ben Dec 04 '20 at 12:42
  • I've removed the extra information to make it easier for you. – Ben Dec 04 '20 at 12:43
  • static classes cant have a private constructor. – Ben Dec 04 '20 at 12:45
  • 1
    true, but the static constructor does not bring **any** benefit over the initialization-method of OP, as the problem is simply related to the test-runner spawning multiple processes. Thus your static constructor **is** invoked multiple times - once per process. – MakePeaceGreatAgain Dec 04 '20 at 12:50
  • https://stackoverflow.com/questions/1873191/why-does-testinitialize-get-fired-for-every-test-in-my-visual-studio-unit-tests – Ben Dec 04 '20 at 12:54