205

I am attempting to unit test a WCF host management engine that I have written. The engine basically creates ServiceHost instances on the fly based on configuration. This allows us to dynamically reconfigure which services are available without having to bring all of them down and restart them whenever a new service is added or an old one is removed.

I have run into a difficulty in unit testing this host management engine, however, due to the way ServiceHost works. If a ServiceHost has already been created, opened, and not yet closed for a particular endpoint, another ServiceHost for the same endpoint can not be created, resulting in an exception. Because of the fact that modern unit testing platforms parallelize their test execution, I have no effective way to unit test this piece of code.

I have used xUnit.NET, hoping that because of its extensibility, I could find a way to force it to run the tests serially. However, I have not had any luck. I am hoping that someone here on SO has encountered a similar issue and knows how to get unit tests to run serially.

NOTE: ServiceHost is a WCF class, written by Microsoft. I don't have the ability to change its behavior. Hosting each service endpoint only once is also the proper behavior...however, it is not particularly conducive to unit testing.

danronmoon
  • 3,814
  • 5
  • 34
  • 56
jrista
  • 32,447
  • 15
  • 90
  • 130
  • 1
    Wouldn't this particular behavior of ServiceHost be something you might want to address? – Robert Harvey Sep 10 '09 at 23:13
  • ServiceHost is written by Microsoft. I have no control over it. And technically speaking, it is valid behavior...you should never have more than one ServiceHost per endpoint. – jrista Sep 10 '09 at 23:30
  • 1
    I had similar issue trying to run multiple `TestServer` in docker. So I had to serialise the integration tests. – h-rai May 29 '19 at 06:21

11 Answers11

304

Each test class is a unique test collection and tests under it will run in sequence, so if you put all of your tests in same collection then it will run sequentially.

In xUnit you can make following changes to achieve this:

Following will run in parallel:

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

To make it sequential you just need to put both the test classes under same collection:

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

For more info you can refer to this link

Vsh
  • 303
  • 2
  • 11
Abhinav Saxena
  • 3,384
  • 1
  • 11
  • 7
  • 56
    Underappreciated answer, I think. Seems to work and I like the granularity, as I have parallelizable and nonparallelizable tests in one assembly. – Igand Sep 02 '18 at 11:22
  • 2
    This is the correct way to do this, ref Xunit documentation. – Håkon K. Olafsen Feb 12 '19 at 10:30
  • 6
    This should be the accepted answer because typically some tests can be run in parallel (in my case all unit tests), but some fail randomly when run in parallel (in my case those using in-memory web client / server), so one is able to optimize test running if one wishes so. – Alexei - check Codidact Jul 11 '19 at 11:22
  • 7
    This did not work for me in a .net core project where I perform integration tests with a sqlite database. The tests were still executed in parallel. The accepted answer did work though. – user1796440 Jul 16 '19 at 08:37
  • Thank you so much for this answer! Needed to do this as I have Acceptance Tests in different classes that both inherit from the same TestBase and the concurrency wasn't playing nice with EF Core. – Kyanite Dec 07 '19 at 22:02
  • In my case the parallel execution in the db context, caused some issues between some shared data, this solved my problem – KARIM Yassine Aug 19 '22 at 12:02
149

Preface: All good unit tests should be 100% isolated. Using shared state (e.g. depending on a static property that is modified by each test) is regarded as bad practice.

I encountered exactly the same issue because my system uses a static service locator (which is less than ideal).

By default xUnit 2.x runs all tests in parallel. This can be modified per-assembly by defining the CollectionBehavior in your AssemblyInfo.cs in your test project.

For per-assembly separation use:

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

or for no parallelization at all use:

[assembly: CollectionBehavior(DisableTestParallelization = true)]

The latter is probably the one you want. More information about parallelisation and configuration can be found on the xUnit documentation.

Squiggle
  • 2,427
  • 1
  • 18
  • 22
  • 9
    For me, there were shared resources between methods within each class. Running a test from one class, then one from another, would break tests from both. I was able to resolve by using `[assembly: CollectionBehavior(CollectionBehavior.CollectionPerClass, DisableTestParallelization = true)]`. Thanks to you, @Squiggle, I can run all my tests and go take a coffee! :) – Alielson Piffer Dec 27 '17 at 16:36
  • 2
    The answer from Abhinav Saxena is more granular for .NET Core. – Yennefer Mar 06 '19 at 16:59
  • 1
    This works for me in .NET Core as well. – Dave Van den Eynde Mar 20 '23 at 08:35
139

For .NET Core projects, create xunit.runner.json with:

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

Also, your csproj should contain

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

For old .Net Core projects, your project.json should contain

"buildOptions": {
  "copyToOutput": {
    "include": [ "xunit.runner.json" ]
  }
}
Dmitry Polomoshnov
  • 5,106
  • 2
  • 24
  • 25
36

For .NET Core projects, you can configure xUnit with an xunit.runner.json file, as documented at https://xunit.net/docs/configuration-files.

The setting you need to change to stop parallel test execution is parallelizeTestCollections, which defaults to true:

Set this to true if the assembly is willing to run tests inside this assembly in parallel against each other. ... Set this to false to disable all parallelization within this test assembly.

JSON schema type: boolean
Default value: true

So a minimal xunit.runner.json for this purpose looks like

{
    "parallelizeTestCollections": false
}

As noted in the docs, remember to include this file in your build, either by:

  • Setting Copy to Output Directory to Copy if newer in the file's Properties in Visual Studio, or

  • Adding

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

    to your .csproj file, or

  • Adding

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

    to your project.json file

depending upon your project type.

Finally, in addition to the above, if you're using Visual Studio then make sure that you haven't accidentally clicked the Run Tests In Parallel button, which will cause tests to run in parallel even if you've turned off parallelisation in xunit.runner.json. Microsoft's UI designers have cunningly made this button unlabelled, hard to notice, and about a centimetre away from the "Run All" button in Test Explorer, just to maximise the chance that you'll hit it by mistake and have no idea why your tests are suddenly failing:

Screenshot with the button circled

DaveShaw
  • 52,123
  • 16
  • 112
  • 141
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
  • @JohnZabroski I don't understand your [suggested edit](https://stackoverflow.com/review/suggested-edits/22900971). What does ReSharper have to do with anything? I think I probably had it installed when I wrote the answer above, but isn't everything here independent of whether you're using it or not? What does the [page you link to in the edit](https://blog.jetbrains.com/dotnet/2011/08/23/new-features-in-resharper-6-unit-test-runner/) have to do with specifying an `xunit.runner.json` file? And what does specifying an `xunit.runner.json` have to do with making tests run serially? – Mark Amery May 01 '19 at 18:20
  • I'm trying to get my tests to run serially, and initially thought the issue was related to ReSharper (since ReSharper does NOT have the "Run Tests in Parallel" button like the Visual Studio Test Explorer does). However, it seems when I use [Theory], my tests are not isolated. This is strange, because everything I read suggests a Class is the smallest parallelizable unit. – John Zabroski May 01 '19 at 18:41
25

This is old question but I wanted to write a solution to people searching newly like me :)

Note: I use this method in Dot Net Core WebUI integration tests with xunit version 2.4.1.

Create an empty class named NonParallelCollectionDefinitionClass and then give CollectionDefinition attribute to this class as below. (The important part is DisableParallelization = true setting.)

using Xunit;

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

After then add Collection attribute to the class which you don't want it to run in parallel as below. (The important part is name of collection. It must be same with name used in CollectionDefinition)

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

When we do this, firstly other parallel tests run. After that the other tests which has Collection("Non-Parallel Collection") attribute run.

  • This is the only solution that worked for me in .net core to run a tests in the same collection sequentially, and parallel with other tests in the same assembly – ihebiheb Dec 05 '21 at 03:12
6

I don't know the details, but it sounds like you might be trying to do integration testing rather than unit testing. If you could isolate the dependency on ServiceHost, that would likely make your testing easier (and faster). So (for instance) you might test the following independently:

  • Configuration reading class
  • ServiceHost factory (possibly as an integration test)
  • Engine class that takes an IServiceHostFactory and an IConfiguration

Tools that would help include isolation (mocking) frameworks and (optionally) IoC container frameworks. See:

Loofer
  • 6,841
  • 9
  • 61
  • 102
TrueWill
  • 25,132
  • 10
  • 101
  • 150
  • I'm not trying to do integration testing. I do indeed need to do unit testing. I am thoroughly versed in TDD/BDD terms and practices (IoC, DI, Mocking, etc.), so the run of the mill kind of stuff like creating factories and using interfaces is not what I am in need of (its already done, except in the case of ServiceHost itself.) ServiceHost isn't a dependency that can be isolated, as it isn't properly mockable (as much of the .NET System namespaces.) I really need a way to run the unit tests serially. – jrista Sep 11 '09 at 05:24
  • 1
    @jrista - no slight on your skills was intended. I'm not a WCF developer, but would it be possible for the engine to return a wrapper around ServiceHost with an interface on the wrapper? Or perhaps a custom factory for ServiceHosts? – TrueWill Sep 11 '09 at 17:13
  • The hosting engine does not return any ServiceHosts. It actually does not return anything, it simply manages the creation, opening, and closing of ServiceHosts internally. I could wrap all the fundamental WCF types, but that is a LOT of work that I haven't really been authorized to do. Also, as it turned out, the problem is not caused by parallel execution, and will still happen during normal operation. I started another question here on SO about the problem, and hopefully I'll get an answer. – jrista Sep 11 '09 at 22:04
  • 1
    @TrueWill: BTW, I wasn't worried about you slighting my skills at all...I just didn't want to get a lot of run-of-the-mill answers that cover all the common stuff about unit testing. I needed a quick answer on a very specific problem. Sorry if I came off a bit gruff, wasn't my intention. I just have pretty limited time to get this thing working. – jrista Sep 11 '09 at 22:05
6

you can Use Playlist

right click on the test method -> Add to playlist -> New playlist

then you can specify the execution order, the default is, as you add them to the play list but you can change the playlist file as you want

enter image description here

HB MAAM
  • 3,913
  • 4
  • 29
  • 39
5

Maybe you can use Advanced Unit Testing. It allows you to define the sequence in which you run the test. So you may have to create a new cs file to host those tests.

Here's how you can bend the test methods to work in the sequence you want.

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

Do let me know whether it works.

Graviton
  • 81,782
  • 146
  • 424
  • 602
  • Great article. I actually had it bookmarked on CP. Thanks for the link, but as it turned out, the problem seems to be much deeper, as test runners do not seem to run tests in parallel. – jrista Sep 11 '09 at 16:20
  • 2
    Wait, first you say you don't want the test to run in parallel, and then you say that the problem is that the test runners don't run tests in parallel... so which is which? – Graviton Sep 12 '09 at 01:32
  • The link you provided no longer works. And is this something you can do with xunit? – Allen Wang Apr 14 '18 at 16:24
3

None of the suggested answers so far worked for me. I have a dotnet core app with XUnit 2.4.1. I achieved the desired behavior with a workaround by putting a lock in each unit test instead. In my case, I didn't care about running order, just that tests were sequential.

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

    [Fact]
    void Test2()
    {
        lock (this)
        {
            //Test Code
        }
    }
}
Matt Hayes
  • 181
  • 1
  • 3
2

I've added the attribute [Collection("Sequential")] in a base class:

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


      public class TestClass1 : SequentialTest
      {
      ...
      }

      public class TestClass2 : SequentialTest
      {
      ...
      }
    }
Matías Romero
  • 1,113
  • 7
  • 6
2

For me, in .Net Core Console application, when I wanted to run test methods ( not classes ) synchronously, the only solution which worked was this described in this blog: xUnit: Control the Test Execution Order

user3057544
  • 807
  • 9
  • 22