Visual Studio 2010 では、 単体テスト機能が Express 版を除き提供されます。今回は単体テストの簡単な使い方のメモを記載します。

1. 単体テストの画面

Visual Studio のテストメニューのウィンドウをマウスでポイントすると、下図のようにいくつかビューが用意されています。

下図はいくつかのウィンドウを表示した状態です。テストリストエディターやテストビュー、テスト結果やテストメソッドのプロパティ画面が表示されます。テスト結果ウィンドウからテストの詳細などを表示することができます。

2. テストプロジェクトの作成

通常のテストプロジェクトの追加でもOK.プロジェクトメニューの新しいテストの追加やテストメニューの新しいテストの追加から作成できます。そのほか、単体テスト対象のクラスを表示して右クリックから、コンテキストメニューの単体テストの作成などいくつかの方法で作成できます。Visual Studio 2010 のエディションによっては前述のメニューが表示されないかもしれません。

3. 単体テストクラスの例

3.1 テスト対象クラス

クラスライブラリプロジェクトを作成して単体テスト用に次のクラスを作成します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SampleLibrary
{
    public class IntCalculator
    {
        public int Add(int a, int b)
        {
            return a + b;
        }
        public int Minus(int a, int b)
        {
            return a - b;
        }
        public int Multiply(int a, int b)
        {
            return a * b;
        }
        public int Divide(int a, int b)
        {
            return a / b;
        }
    }
}

コードエディタ上で右クリック→単体テストの作成から単体テストクラスを作成できますが、今回は、プロジェクトの追加でプロジェクトテンプレートでテストを選択して単体テストプロジェクトを新規作成します。

3.2 単体テストクラス

クラスライブラリと同じソリューション上で、別にテストプロジェクトを作成します。クラスライブラリへの参照の追加が存在しない場合は、プロジェクトの参照を追加しておきます。テスト用に作成したクラスは次の通りです。テストクラスには TestClass アトリビュートで修飾します。単体テストメソッドはTestMethod アトリビュートを付与します。TestProperty アトリビュートをセットするとテストリストエディターからテストメソッドを選択するなどしてプロパティウィンドウを表示したときに、追加のプロパティとして表示されます。

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SampleLibrary;

namespace SampleLibraryTest
{
    [TestClass]
    public class IntCalculatorTest
    {
        /// <summary>
        /// TestContextというプロパティを作成しておく
        /// と単体テストメソッドでTestContextを参照できる。
        /// </summary>
        public TestContext TestContext { get; set; }

        /// <summary>
        /// テストプロジェクトのアセンブリで全テストクラスの単体テスト実施前に1回呼び出される
        /// </summary>
        /// <param name="context"></param>
        [AssemblyInitialize]
        public static void AssemblyInitialize(TestContext context)
        {
            context.WriteLine("AssemblyInitialize");
        }
        /// <summary>
        /// テストプロジェクトのアセンブリで全テストクラスの単体テスト実施後に1回呼び出される
        /// </summary>
        /// <param name="context"></param>
        [AssemblyCleanup]
        public static void AssemblyCleanup()
        {
        }
        /// <summary>
        /// テストクラス内のすべての単体テスト実施前に1回呼び出される
        /// </summary>
        /// <param name="context"></param>
        [ClassInitialize]
        public static void ClassInitialize(TestContext context)
        {
            // デモ用に追加
            context.WriteLine("ClassInitialize");
        }
        /// <summary>
        /// テストクラス内のすべての単体テスト実施後に1回呼び出される
        /// </summary>
        /// <param name="context"></param>
        [ClassCleanup]
        public static void ClassCleanup()
        {
            // Do Nothing
        }
        /// <summary>
        /// 各単体テストメソッド呼び出し直前に呼び出される
        /// </summary>
        [TestInitialize]
        public void TestInitialize()
        {
            // デモ用に追加
            this.TestContext.WriteLine("Test Initialize");
        }
        /// <summary>
        /// 各単体テストメソッド呼出し直後に呼び出される
        /// </summary>
        [TestCleanup]
        public void TestCleanup()
        {
            // デモ用に追加
            this.TestContext.WriteLine("Test Cleanup");
        }
        [TestMethod]
        [TestProperty("詳細", "コンストラクタのテスト")]
        public void IntCalculatorConstructorTest()
        {
            // Arrange
            // Act
            IntCalculator calc = new IntCalculator();
            // Assert
        }
        [TestMethod]
        [TestProperty("詳細", "整数の加算のテスト")]
        public void AddTest()
        {
            // デモ用に追加
            this.TestContext.WriteLine("AddTest");
            // Arrange
            int expected = 5;
            IntCalculator calc = new IntCalculator();
            // Act
            int actual = calc.Add(2, 3);
            // Assert
            Assert.AreEqual(expected, actual);
        }

        [TestMethod]
        [TestProperty("詳細", "0除算の例外テスト")]
        [ExpectedException(typeof(DivideByZeroException))]
        public void DivideTest()
        {
            // デモ用に追加
            this.TestContext.WriteLine("DivideTest");
            // Arrange
            IntCalculator calc = new IntCalculator();
            // Act
            int actual = calc.Divide(10, 0);
        }

    }
}

4. 単体テストの実行

単体テストは、単体テストクラスをエディタで表示して右クリック→テストの実行や、テストビューからテストを選択してツールバーのテストの実行から実行できます。テストリストエディタでテストセットを定義しておきて、そのまとまり単位でテストリストエディタのツールバーから実行することができます。

5. テストの実行順序とテスト用のクラス

テストメソッドは、 AssemblyInitialize → ClassInitialize  → TestInitialize → TestMethod → TestCleanup → ClassCleanup → AssemblyInCleanup の順で属性が付与されたメソッドが実行されます。 AssemblyInitalize, AssemblyCleanup は テストプロジェクト内で最も最初と最後に1度だけ実行されます。 ClassInitialize , ClassCleanup は テストクラスの単体テストメソッドを実行する最も最初と最後に1度だけ呼び出されます。 TestInitialize, TestCleanup は TestMethod の付与された単体テストを実行する前後に毎回呼び出されます。そのほか有用なクラスの簡単な説明を記載します。詳細は Help を参照して下さい。

クラス名 説明
Assert アサートメソッドが定義されたクラス
CollectionAssert コレクション用のアサートメソッドが定義されたクラス
TestContext 単体テストのコンテキストクラス。デバッグログや実行情報などのメソッドが定義されている。テストクラスにプロパティTestContext を定義しておくと、単体テストメソッドで参照できるようになる。
ExpectedExceptionAttribute 単体テストメソッドが例外を発生させた場合に正常であると判定する場合に、単体テストメソッドに付与する。引数に例外の型などを指定する。
TestPropertyAttribute サンプルで例示しているように、テストメソッドに付与すると、設定値がプロパティウィンドウに表示されるようになります。テストリストウィンドウなどでテストメソッドを選択したときにプロパティウィンドウを表示すると、設定したプロパティ名と値が表示されるようになる。

規定では、テストのログは、ソリューション直下の TestResults フォルダに作成されます。メモ帳で表示することもできます。

簡単ですが、説明は以上です。