0. 前置き
ちょっと前の話ですが、.NET Frameworkで利用できるユニットテストフレームワークのNUnitがver.3にバージョンアップしました。2015年11月です。作者曰く、次の世代に耐えうるユニットテストフレームワークにするために、コードを最初から全て書き直したらしいです。機能としての、特徴的な変更点は大きく3つあるようです。(リリースノートが膨大すぎるので、大事なことが漏れていたらごめんなさい)
1.テストを並列実行できるようにした
2.拡張ポイントを増やした(Framework Extensibility)
例)Assertで使える機能を開発者が簡易に作成できる。カスタム属性を作成できる。
3.テストエンジンを作りやすくした(Engine Extensibility)
それぞれを解説してみたいのですが、今回はまず1のテストの並列実行について試してみます。
1. 利用するコード
並列実行を試すコードは以下のものを利用します。テストメソッドの中は、直列と並列で結果が変化し分かりやすいように、「5秒間停止する処理」のみを入れています。つまり、直列でテストを実行すると15秒かかるわけです。実際はNUnitを走らせるオーバーヘッドがあるため、テスト開始から終了までは、15+α秒かかります。(NUnit3を使ったテストの実行方法については、こちらで説明しています。)
using NUnit.Framework;
namespace AwesomeApp.Test
{
[TestFixture]
public class CalculatorTest_A
{
[Test]
public void AddTest_Succeess_A()
{
System.Threading.Thread.Sleep(5000);
}
}
[TestFixture]
public class CalculatorTest_B
{
[Test]
public void AddTest_Succeess_B()
{
System.Threading.Thread.Sleep(5000);
}
}
[TestFixture]
public class CalculatorTest_C
{
[Test]
public void AddTest_Succeess_C()
{
System.Threading.Thread.Sleep(5000);
}
}
}
実行した結果は、下記のとおり。合計19秒かかっています。(オーバーヘッドは4秒と推察)
2. 並列実行させる
並列実行させるテストクラスに、Parallelizable属性を付与します。
using NUnit.Framework;
namespace AwesomeApp.Test
{
[Parallelizable]
[TestFixture]
public class CalculatorTest_A
{
[Test]
public void AddTest_Succeess_A()
{
System.Threading.Thread.Sleep(5000);
}
}
[Parallelizable]
[TestFixture]
public class CalculatorTest_B
{
[Test]
public void AddTest_Succeess_B()
{
System.Threading.Thread.Sleep(5000);
}
}
[Parallelizable]
[TestFixture]
public class CalculatorTest_C
{
[Test]
public void AddTest_Succeess_C()
{
System.Threading.Thread.Sleep(5000);
}
}
}
実行結果は9秒になりました。同様にオーバーヘッドは4秒とすると、3つのテストが並列で実行された結果だと分かります。
3. 細かな設定など
テストメソッドの並列はサポートされていない
テストクラス(TestFixture)間の並列実行がサポートされています。テストクラス内のそれぞれのテストメソッドの並列実行は行われません。(テストメソッドの並列実行については将来的にサポートされるようです。)
並列実行数の制御
いくつのテストを並列で動かすかの設定は、テストが含まれるプロジェクトのAssemblyInfo.csファイルで定義します。
[assembly: LevelOfParallelism(8)]
この設定だと、最大8並列でテストが実行されます。
なお、未定義の場合は、最大並列数は、PCの論理プロセッサ数(Environment.ProcessorCount)になるようです。
並列実行による弊害
今回記載した方法をとればテストを並列して実行できますが、そもそもテスト自体が並列実行に適した形で書かれている必要があります。例えば、複数のテストクラスが、1つのデータベースの値の読み書きをするなど、並列化を考慮していない場合は、テストが失敗します。
4. おわりに
簡易な方法で並列実行が可能になりました!
ひょっとしたらテストのかなりの高速化が望めるかもしれないです。