NUnitで自作クラスのユニットテストを行う

ギークもすなるユニットテストというものをおっさんもしてみんとてするなり。
ユニットテストを書くことで、仕様を頭の中で整理しつつ安心して実装や保守を最短距離で進めていきたい。
備忘録も兼ねてメモ。


とりあえず[TestCase]属性と Assert.Equal() でテストメソッドを覚えればテストは書けそうですね。

私が愛用しているSharpDevelop 4.0 では [TestCase]属性がツリーに表示されない (テストはできます)ので、NUnit-Guiを使います。

# 概要

テスト対象のプロジェクト名をここでは「RenameAlignDigit」とします。

1. NUnitのインストール
2. ユニットテスト用プロジェクト「～Test」の作成
	* RenameAlignDigit     (テスト対象のプロジェクト)
	* RenameAlignDigitTest (ユニットテスト用プロジェクト) 

3. RenameAlignDigitTest に対して参照の追加
	* nunit.framework.dll (NUnitのDLL)
	* RenameAlignDigit (テスト対象プロジェクト)

4. Publicでないメソッドをテストしたい場合には、更に以下の設定が必要
	*  テストされる側「RenameAlignDigit」の AssemblyInfo.cs に InternalVisibleTo("RenameAlignDigitTest") 属性を追加

5. テストしたいメソッドに対応するテストメソッドを書く
	*  新規作成 -> ユニットテストを追加
	*  [Test] 属性をつけたメソッドを書き、中に・・・
	*  Assert.Equal(_期待される値_, _実際の値_); を書く

6. NUnit-Guiからテストの実行
	* RenameAlignDigitTest.Dll を読み込む
	* Run してテスト結果を確認する 

# 詳細

## 1. NUnitのインストール

特につまる所もないと思うので省略。
ちなみに、「win NUnit-2.6.1.msi」をインストールしました。

* [NUnit - Download](http://www.nunit.org/index.php?p=download) からダウンロード
* インストール時はTypicalを選択で特に問題なし

## 2. ユニットテスト用プロジェクト「～Test」の作成

テスト対象のプロジェクトはこれです。

![テスト対象のプロジェクト](https://lh6.googleusercontent.com/-FKWV-OZ6Ols/UDCxHpRLYFI/AAAAAAAAEiQ/sFwmaSiJB9I/s800/2012-08-19_18h25_16.png)

```csharp:ClassMyCompare.cs 
using System;

namespace Rohinomiya
{
	public class MyCompare
	{
		public MyCompare()
		{
		}

		/// <summary>
		/// 数値 current がlower ～ higher の範囲内か？		
		/// </summary>
		/// <param name="lower">区間(開始)</param>
		/// <param name="current">比較される値</param>
		/// <param name="higher">区間(終了)</param>
		/// <param name="inclusive">閉区間か？(境界値を含む) (初期値:true)</param>
		/// <returns>範囲内であればtrue</returns>
		public static bool Between(int lower, int current, int higher, bool inclusive = true)
		{
			if(lower > higher) Swap(ref lower,ref higher);
		
			return inclusive ?
				(lower <= current && current <= higher) :
				(lower <  current && current <  higher);
		}
		
		/// <summary>
		/// ２つの値を交換する
		/// </summary>
		/// <param name="a">値A</param>
		/// <param name="b">値B</param>
		public static void Swap(ref int a, ref int b)
		{
			int temp = a;
			a = b;
			b = temp;
		}
	}
}
```

これに対するユニットテスト用プロジェクトを新規作成します。

 * NUnit-Guiで使うために、.DLLとして作成します。
 * 元のプロジェクト名の後ろに「～Test」をつけます

プロジェクト新規作成 -> クラスライブラリを選択 -> プロジェクト名＝RenameAlignDigitTest

## 3. 2.に対して参照の追加

プロジェクト「RenameAlignDigitTest」に以下の参照を追加します。

1. nunit.framework.dll (NUnitのDLL)

	参照の追加 -> .NETアセンブリブラウザ -> 参照 -> nunit.framework.dll を指定(C:\Program Files\NUnit ...\bin\framework\nunit.framework.dll)

2. RenameAlignDigit (テスト対象プロジェクト)

	参照の追加 -> プロジェクト -> RenameAlignDigit 

![](https://lh6.googleusercontent.com/-fPXzeaxd1fI/UDC25O7J80I/AAAAAAAAEiw/CoVpz7BLSpI/s800/2012-08-19_18h31_44.png)

## 4. InternalVisibleToアセンブリ属性の追加

Publicでないメソッドをテストしたい場合には、更に以下の設定が必要。

* テストされる側「RenameAlignDigit」の AssemblyInfo.cs に InternalVisibleTo("RenameAlignDigitTest") 属性を追加

```csharp:AssemblyInfo.cs 
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("RenameAlignDigitTest")]
```

RenameAlignDigitTest になら、私見せてもいいわ・・・ということですね。

## 5. テストメソッドを書く

* テストしたいメソッドに対応するテストメソッドを書きます。
* テストメソッド名は、元のメソッド名＋「Test」
* [Test] 属性で、1メソッド1テスト。
* [TestCase] 属性でパラメータを書けば、そのパターン分テストを繰り返してくれます。こちらの方が便利。
* Assert.AreEqual(_期待される_, _値実行結果_); でテストする
* Assertクラスには、他にもテスト用のメソッドがいろいろありますが、最低限これだけ覚えればテストは記述できるかと。

```csharp:ClassMyCompareTest.cs 
using System;
using NUnit.Framework;

namespace Rohinomiya
{
	[TestFixture]
	public class MyCompareTest
	{
//		[Test]
//		public void SwapTest()
//		{
//			int a = 3;
//			int b = 5;
//			
//			MyCompare.Swap(ref a,ref b);
//			Assert.AreEqual(5,a);
//			Assert.AreEqual(3,b);
//			
//			a = -1;
//			b = 0;
//			
//			MyCompare.Swap(ref a,ref b);
//			Assert.AreEqual(0,a);
//			Assert.AreEqual(-1,b);
//		}

		[TestCase(3,5,5,3)]
		[TestCase(-1,0,0,-1)]
		public void SwapTest(int expectedA, int expectedB, int a, int b)
		{
			MyCompare.Swap(ref a,ref b);

			Assert.AreEqual(expectedA,a);
			Assert.AreEqual(expectedB,b);
		}

//		[Test]
//		public void BetweenTest()
//		{
//			Assert.AreEqual(true,MyCompare.Between(1,2,3));
//			Assert.AreEqual(true,MyCompare.Between(1,3,3));
//			Assert.AreEqual(true,MyCompare.Between(1,1,3));
//			
//			Assert.AreEqual(false,MyCompare.Between(1,1,3,false));
//			Assert.AreEqual(false,MyCompare.Between(1,3,3,false));
//			Assert.AreEqual(true,MyCompare.Between(1,2,3,false));
//
//			Assert.AreEqual(true,MyCompare.Between(3,2,1));
//			Assert.AreEqual(true,MyCompare.Between(3,3,1));
//			Assert.AreEqual(true,MyCompare.Between(3,1,1));
//			
//			Assert.AreEqual(false,MyCompare.Between(3,1,1,false));
//			Assert.AreEqual(false,MyCompare.Between(3,3,1,false));
//			Assert.AreEqual(true,MyCompare.Between(3,2,1,false));
//		}

		[TestCase(true,1,2,3,true)]
		[TestCase(true,1,3,3,true)]
		[TestCase(true,1,1,3,true)]
		[TestCase(false,1,1,3,false)]
		[TestCase(false,1,3,3,false)]
		[TestCase(true,1,2,3,false)]
		[TestCase(true,3,2,1,true)]
		[TestCase(true,3,3,1,true)]
		[TestCase(true,3,1,1,true)]
		[TestCase(false,3,1,1,false)]
		[TestCase(false,3,3,1,false)]
		[TestCase(true,3,2,1,false)]
		public void BetweenTest(bool expected, int lower, int current, int higher, bool inclusive = true)
		{
			bool result = MyCompare.Between(lower, current, higher, inclusive);
			Assert.AreEqual(expected, result);
		}

	}
}
```

TestCaseではデフォルトパラメータが使えないのかな。うまく行かなかったのでその分まで書きました。

### プロジェクトの最終的なイメージ

最終的にはこんな感じになります。

![](https://lh3.googleusercontent.com/-4q_ezKyegTA/UDC24yZrNWI/AAAAAAAAEis/-ZFX2JWkvvY/s800/2012-08-19_18h49_10.png)

# 6. NUnit-Guiからテストの実行

(事前に一度ビルド通しておいてください)

1. プログラムメニューから、NUnitを起動する。
2. File -> Open Project -> RenameAlignDigitTest.dll を開く
3. 「Run」をクリックして、テスト実行する
4. プロジェクトを保存(*.nunit)しておけばまたすぐテストできます。

![](https://lh6.googleusercontent.com/-YJW_6072Dps/UDC680Ge90I/AAAAAAAAEjQ/TPuaSJ4gp6k/s800/2012-08-19_19h05_29.png)

# 参考

* [VB2010 Express + NUnit 2.5 で、 初めてのTDD Step by Step: TDD.NET](http://www.tdd-net.jp/vb2010ee-nunit25-tdd-stepbystep.html)

* [NUnit を使ってみる | アカベコマイリ](http://akabeko.me/blog/2011/02/nunit-%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B/)

* [＠IT：.NET Tools ： テスト駆動開発ツール最前線（前編）](http://www.atmarkit.co.jp/fdotnet/tools/nunit22_01/nunit22_01_01.html)

* [NUnit 2.5 RC - ZOETROPEの日記](http://d.hatena.ne.jp/ZOETROPE/20090521)