1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MSTestやxUnit.netと比較したときのNUnitの特異な点

Last updated at Posted at 2025-06-17

この文書について

他のテストフレームワークを使った経験のある人が、はじめてNUnitを使うことになったときのための文書である。

NUnitの特徴

NUnitを使いはじめるまえに、他のテストフレームワークとは思想の異なる点がひとつ存在することを知っておくと役に立つ。

ここで私が言いたいのは、

  • Assertの書き方を比較してこうした特徴があるよ
  • テストメソッドごとの初期化、後始末のメソッドはこうやって書くよ

ということではない。

もっと基本的なテストフレームワークの動作、仕様についてである。
すなわち、NUnitは(デフォルトでは)、テストメソッドごとにテストクラスの新しいインスタンスを作成しない。言い換えると、NUnitは、そのテストクラスに含まれるすべてのテストメソッドを単一のインスタンスを使用して実行する

実際のテストコード

上記のような特徴があるので、以下のテストは失敗する。
TestFixture1は一度しかインスタンスが作成されないので、Test1Test2とで後に実行されるメソッドではCountの値が1にならない。

// For NUnit
public class TestFixture1
{
	int Count { get; set; } = 0;

	[Test]
	public void Test1()
	{
		Count++;
		Assert.That(Count, Is.EqualTo(1));
	}

	[Test]
	public void Test2()
	{
		Count++;
		Assert.That(Count, Is.EqualTo(1));
	}
}

以下は同じことをMSTestでおこなったコードであるが、このテストは成功する。
MSTestはTestMethod1, TestMethod2を異なるインスタンスを使用して実行するのだ。
コードはあげないが、xUnit.netでも同様だ。

// For MSTest
[TestClass]
public class MSTest1
{
	int Count { get; set; } = 0;

	[TestMethod]
	public void TestMethod1()
	{
		Count++;
		Assert.AreEqual(1, Count);
	}

	[TestMethod]
	public void TestMethod2()
	{
		Count++;
		Assert.AreEqual(1, Count);
	}
}

対応方法

NUnit3.12までは、インスタンス変数を初期化するため、NUnitユーザは逐一、

// For NUnit
public class TestFixture1
{
	int Count { get; set; } = 0;

	[SetUp]
	public void Init() => Count = 0;

    ...
}

といったように、初期化のためのSetUpメソッドを書いていた(多分)。
SetUp属性を持つメソッドは、そのクラスのすべてのテストメソッドの前に実行される。

NUnit3.13とそれ以降では、以下のように、テストメソッドごとにテストクラスの新しいインスタンスを作成するように設定できるようになった。

// For NUnit 3.13-
[FixtureLifeCycle(LifeCycle.InstancePerTestCase)]
public class TestFixture1
{
    ...
}

このFixtureLifeCycle属性はアセンブリ単位で指定することもできる。

[assembly: FixtureLifeCycle(LifeCycle.InstancePerTestCase)]

こうしておけば、もうMSTestやxUnit.netの動作と大差がないことになる。
この設定は、テストを並列実行したい場合にも有用なものになるだろう。

NUnit開発者はどう考えているか

この点は、他のテストフレームワークに習熟していればいるほど、NUnitを使う上ではまるポイントだと思う。
MSTestやxUnit.netはもちろん、JUnitなどとも異なる仕様だからだ。

まさにこの仕様について、初期バージョンの開発者のひとりであるJames Newkirk氏が「最大の失敗のひとつ(one of the biggest screw-up)」「私の誤り(I think this one was my fault.)」と語っている。
https://learn.microsoft.com/ja-jp/archive/blogs/jamesnewkirk/why-variables-in-nunit-testfixture-classes-should-be-static

氏はxUnit.netのプロジェクトの立ち上げにも関係しているが、そこでは「Single Object Instance per Test Method.」と宣言されている。
https://jamesnewkirk.typepad.com/posts/2007/09/announcing-xuni.html

JUnitはなぜテストクラスのインスタンスを使いまわさなかったのか

JUnitはテストメソッドごとにテストクラスの新しいインスタンスを作成する。
MSTestやxUnit.netが同様の動きをするのはその影響だ。
では、どうしてJUnitはテストクラスのインスタンスを使い回さなかったのだろう。
それは、テスト間がお互いに影響することのない状態、「分離」を実現するためだ。

詳しくはMartin Fowler氏のブログを読んで欲しい。
https://martinfowler.com/bliki/JunitNewInstance.html
この和訳が以下で読める。
https://bliki-ja.github.io/JunitNewInstance

参考文書

この文書のNUnitのFixtureLifeCycleについては、以下のドキュメントを参考にしている。
https://docs.nunit.org/articles/nunit/writing-tests/attributes/fixturelifecycle.html

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?