ツナ缶雑記

ぐうたらSEのブログです。主にマイクロソフト系技術を中心に扱います。

xUnitで単体テストの並列実行をオフにする

f:id:masatsuna:20210303024437p:plain

xUnit を使って C#単体テストを作り、全テストをまとめて実行すると、一部の単体テストが並列実行される様子がわかります。 もう少し正確に言うと、 1 つのテストクラス内のテストは直列実行されますが、異なるテストクラスに配置されているテストは並列実行されます。 このあたりの挙動についてまとめてみようと思います。

環境

テストコレクション

冒頭に述べた挙動は、 xUnit の持つテストコレクションという概念で説明ができます。 テストコレクションは、動画サイトにおける再生リストのようなもので、同一のテストコレクションの中に含まれているテストは、直列実行されるようになっています。 しかし、テストコレクションが異なれば、お互いにテストは並列に実行されるようになっています。

1 つのテストクラス内に含まれるテストメソッドは、すべて同じテストコレクションに含まれます。 特に何も指定しなければ、テストクラス単位でテストコレクションが作成されます。

コードレベルで見ると、以下のように 1 つのテストクラス内にあるテストメソッドは、テストコレクションが同一になるため、順次実行されます。

public class TestClass1
{
    // Test1, Test2 は順次実行(順不同)

    [Fact]
    public void Test1()
    {
        // Do something...
    }

    [Fact]
    public void Test2()
    {
        // Do something...
    }
}

それに対して、以下のようにテストクラスを分割すると、各テストメソッドはテストコレクションが別になるため、並列実行されます。

// Test1, Test2 は並列実行

public class TestClass1
{
    [Fact]
    public void Test1()
    {
        // Do something...
    }
}

public class TestClass2
{
    [Fact]
    public void Test2()
    {
        // Do something...
    }
}

テストを直列実行できるようにする

テストを直列実行するためには、いくつかの方法があります。

テストコレクションをまとめる方法

テストコレクションが 1 つであれば、テストメソッドは直列実行されるようになります。 テストクラスに対して CollectionAttribute を付与することで、テストコレクションを明示的に指定することができます。 CollectionAttribute の引数にテストコレクションの名前を与えることができます。 テストクラスに対して同名のテストコレクションであることをマークすることで、テストコレクションを 1 つにまとめることができます。

[Collection("テストコレクションHoge")]
public class TestClass1
{
    [Fact]
    public void Test1()
    {
        // Do something...
    }
}

[Collection("テストコレクションHoge")]
public class TestClass2
{
    [Fact]
    public void Test2()
    {
        // Do something...
    }
}

このようにテストコレクションをまとめておけば、テストは直列実行されます。

テストクラス単位にテストコレクションが作られないようにする方法

テストクラス単位にテストコレクションが作られてしまう既定の動作を変更することができます。

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

このようなコードを追加することで、テストコレクションの単位がアセンブリ単位になります。 こうすることでテストが直列実行されるようになります。

テスト実行のスレッド数を制御する方法

テストが並列実行されるのは、テストランナーが複数のスレッドを起動しているからにすぎません。 なのでスレッドの数を制限することでも、テストが直列実行できます。

using Xunit;
[assembly: CollectionBehavior(MaxParallelThreads = 1)]

この例ではスレッドを 1 つに制限しているので、テストコレクションの単位とは関係なく、テストは直列実行されます。

並列処理を無効に設定する方法

そもそもテストの並列実行機能をオフに設定することもできます。

using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)]

まとめ

xUnit のテスト並列実行についてまとめました。 特にデータベースに依存するようなテストを xUnit で書いていると、こういった問題にぶち当たるかと思います*1。 安易に並列実行をオフにすることは推奨しませんが、いざというケースでは使えるのではないでしょうか。

参考資料

xunit.net

*1:そんなテストコードを xUnit で書くな、という話はもちろんありますけどね。