テスト

単体テストの作成

対象とするクラスにTestClass属性を、メソッドにTestMethod属性を指定します。

public class MyClass
{
    static public int Method()
    {
        return 0;
    }
}

[TestClass]
public class UnitTest
{
    [TestMethod]
    public void TestMethod1
    {
        int a = MyClass.Method();

        Assert.AreEqual(1, a);
        // Assert.AreEqual に失敗しました。<1> が必要ですが、<0> が指定されました。
    }
}

関連するクラス

クラス テスト対象
Assert 一般
CollectionAssert コレクション
StringAssert 文字列
AssertFailedException 例外
AssertInconclusiveException  
UnitTestAssertException  
Microsoft.VisualStudio.TestTools.UnitTesting Namespace | Microsoft Docs 単体テスト フレームワーク | Microsoft Docs

関連する属性

属性 意味
TestClassAttribute このクラスは、テスト対象である
TestMethodAttribute このメソッドは、テスト対象である
ExpectedExceptionAttribute このテストから例外が投げられることが期待される
IgnoreAttribute このテストは一時的に無視される
 
テストの識別および並べ替えのための属性 - 単体テスト フレームワーク | Microsoft Docs

TestMethodAttribute

テスト対象とするメソッドは、以下の条件を満たす必要があります。

  • TestMethodAttributeで印づけられている
  • staticではない
  • publicである
  • 戻り値はvoid
  • 引数を取らない
Asynchronous unit tests with .NET 4.0 - Stack Overflow

この条件を満たすメソッドは、次のような形式となります。

[TestMethod]
public void TestMethod()
{
}

Assertクラス

一般的なオブジェクトをテストできます。

検証内容 メソッド
肯定 否定
同一の値であるか AreEqual() AreNotEqual()
同一のオブジェクトであるか AreSame() AreNotSame()
指定の型であるか IsInstanceOfType() IsNotInstanceOfType()
trueであるか IsTrue() IsFlase()
nullであるか IsNull() IsNotNull()
メソッド - Assert クラス (Microsoft.VisualStudio.TestTools.UnitTesting) | MSDN

このAssertクラスは、DebugクラスのAssert()メソッドとは異なります。

コレクションの検証にはCollectionAssert、文字列の特殊な検証にはStringAssertを用います。

AreEqual()

Assert.AreEqual メソッド (Microsoft.VisualStudio.TestTools.UnitTesting) | MSDNにあるように、多数のオーバーロードがあります。

2つのオブジェクトが、同一であることを検証

public static void AreEqual(
    Object expected, // テストで予期するオブジェクト
    Object actual    // テストで生成したオブジェクト
)
Assert.AreEqual メソッド (Object, Object) (Microsoft.VisualStudio.TestTools.UnitTesting) | MSDN

浮動小数点数は演算時に丸めによって誤差が生じるため、精度を指定できるAreEqual(Double, Double, Double)の形式で検証します。

2つのジェネリック型データが、同一であることを検証

public static void AreEqual<T>(
    T expected, // テストで予期するジェネリック型データ
    T actual    // テストで生成したジェネリック型データ
)
Assert.AreEqual(T) メソッド (T, T) (Microsoft.VisualStudio.TestTools.UnitTesting) | MSDN

2つの倍精度浮動小数点数が、指定された精度内にあることを検証

public static void AreEqual(
    double expected, // テストで予期する値
    double actual,   // テストで生成した値
    double delta     // 精度
)
Assert.AreEqual メソッド (Double, Double, Double) (Microsoft.VisualStudio.TestTools.UnitTesting) | MSDN

2つの文字列が、同一であることを検証

public static void AreEqual(
    string expected, // テストで予期する値
    string actual,   // テストで生成した値
    bool ignoreCase  // trueならば、大文字/小文字を区別しない
)
Assert.AreEqual メソッド (String, String, Boolean) (Microsoft.VisualStudio.TestTools.UnitTesting) | MSDN

このメソッドはZERO WIDTH NO-BREAK SPACEのような特殊文字を含むとき、第3引数を指定しないメソッドとは異なる結果を返します。

string str1 = new String(new char[] { 'A' });
string str2 = new String(new char[] { 'A', '\uFEFF' }); // U+FEFF (ZERO WIDTH NO-BREAK SPACE)

Assert.AreEqual("A", str1, false); // 成功
Assert.AreEqual("A", str2, false); // 成功

Assert.AreEqual("A", str1); // 成功
Assert.AreEqual("A", str2); // Assert.AreEqual に失敗しました。<A> が必要ですが、<A> が指定されました。

CollectionAssertクラス

コレクションをテストできます。

検証内容 メソッド
肯定 否定
同一であるか (同じ値の要素が同じ順番で同じ数ある) AreEqual() AreNotEqual()
等価であるか (同じ値の要素が同じ数ある) AreEquivalent() AreNotEquivalent()
指定の要素を含むか Contains() DoesNotContain()
別のコレクションのサブセット (部分集合) であるか IsSubsetOf() IsNotSubsetOf()
検証内容 メソッド
すべての要素が指定の型であるか AllItemsAreInstancesOfType()
すべての要素がnullでない AllItemsAreNotNull()
すべての要素が一意であるか (同一の要素がない) AllItemsAreUnique()
メソッド - CollectionAssert クラス (Microsoft.VisualStudio.TestTools.UnitTesting) | MSDN

AreEqual()

public static void AreEqual (
    System.Collections.ICollection expected,
    System.Collections.ICollection actual
    );
AreEqual(ICollection, ICollection) - CollectionAssert.AreEqual Method (Microsoft.VisualStudio.TestTools.UnitTesting) | Microsoft Docs
int[] actual = MyMethod();

int[] expected = new int[]{ 1, 2, 3 };
CollectionAssert.AreEqual(expected, actual);

複雑なコレクションは、IComparerを引数に取るメソッドで検証します。

public static void AreEqual (
    System.Collections.ICollection expected,
    System.Collections.ICollection actual,
    System.Collections.IComparer comparer
    );

この場合には比較用に、IComparerを実装したクラスを用意します。

public class MyComparer : IComparer
{
    int IComparer.Compare(Object x, Object y)
    {
        return (x == y) ? 0 : 1;
    }
}

StringAssertクラス

文字列をテストできます。単純に等しいかどうか検証するだけならば、AssertクラスのAreEqual()を用います。

検証方法 メソッド
肯定 否定
指定の正規表現に一致するか Matches() DoesNotMatch()
検証方法 メソッド
指定の文字列を含むか Contains()
指定の文字列で始まるか StartsWith()
指定の文字列で終わるか EndsWith()
メソッド - StringAssert クラス (Microsoft.VisualStudio.TestTools.UnitTesting) | MSDN

ExpectedExceptionAttribute

例外の検証には、ExpectedExceptionAttribute属性に指定します。ExpectedExceptionAttribute クラス (Microsoft.VisualStudio.TestTools.UnitTesting) | MSDN

この属性が付けられたメソッドでは、指定の例外が投げられないとテストに失敗します。

[TestMethod]
[ExpectedException(typeof(System.Exception))]
public void TestMethod1()
{
}

たとえばこの場合は、「テスト メソッド TestMethod1 は例外をスローしませんでした。」「テスト メソッドで定義されている属性 ExpectedExceptionAttribute で例外が予期されていました。」として失敗します。

[TestMethod]
[ExpectedException(typeof(System.Exception))]
public void TestMethod2()
{
    throw new System.Exception();
}

一方で例外が投げられれば、テストに成功します。このとき例外はメソッドの外に投げられる必要があるため、メソッド内で捕捉しては失敗となります。

[TestMethod]
[ExpectedException(typeof(System.Exception))]
public void TestMethod3()
{
    try
    {
        throw new System.Exception();
    }
    catch (Exception) { }
}

また例外が投げられることを期待する処理は、テストメソッドの最後にします。さもなくばそれ以降の処理が評価されないまま、テストに成功してしまうことになります。

[TestMethod]
[ExpectedException(typeof(System.Exception))]
public void TestMethod2()
{
    throw new System.Exception();

    Assert.Fail(); // ここが評価されないまま、テストに成功する
}

テスト対象の選択

IgnoreAttribute

Ignore属性をテストメソッドに追加することで、そのテストを一時的に無効にできます。

[TestMethod]
[Ignore]
public void TestMethod1()
{
}
IgnoreAttribute クラス (Microsoft.VisualStudio.TestTools.UnitTesting) | MSDN

無効化したテストを再び有効にするには、Ignore属性を削除するか、コメントアウトします。方法 : テストを無効または有効にする | MSDN

TestMethod属性を削除することでもテストを無効にできますが、それではテストの存在そのものが隠されてしまうため、Ignore属性を用いるようにします。

複数テストの有効・無効を一括して切り替えるには、#ifディレクティブでIgnore属性の指定を制御するか、TestCategory属性でテストをまとめます。c# - Ignore IgnoreAttribute - Stack Overflow

TestCategoryAttribute

テスト対象に共通の名前を付加することで、それらをグループ化して一括して選択できます。TestCategoryAttribute Class (Microsoft.VisualStudio.TestTools.UnitTesting) | Microsoft Docs

[TestMethod]
[TestCategory("CategoryName")]
public void TestMethod1()
{
}

この指定に対して、Visual StudioではTrait:"CategoryName"のようにフィルタを指定することで、CategoryNameだけのテストを抽出できます。

PriorityAttribute

テストに優先順位を付けることができます。PriorityAttribute Class (Microsoft.VisualStudio.TestTools.UnitTesting) | Microsoft Docs

これは順序指定テスト (ordered test) や、順位によるフィルタとして利用できます。What is PriorityAttribute Used for in MSTEST / Visual Studio - Stack Overflow

初期化と終了処理

下表の属性を付加することで、テストに共通して必要な処理を行うメソッドを定義できます。

属性 適用回数 適用条件 実行時機
AssemblyInitializeAttribute アセンブリ内で1つだけ staticメソッドで、TestContextを引数にとる アセンブリのすべてのクラスの読み込み前
ClassInitializeAttribute クラス内で1つだけ クラスのすべてのテストメソッドの読み込み前
TestInitializeAttribute 制限なし なし クラスのテストメソッドの読み込み前
TestCleanupAttribute クラスのテストメソッドの実行後
ClassCleanupAttribute クラス内で1つだけ staticメソッド クラスのすべてのテストメソッドの実行後
AssemblyCleanupAttribute アセンブリ内で1つだけ アセンブリのすべてのクラスの実行後

たとえば1つのクラス内にテスト対象のメソッドが2つあるとき、これらの属性を付加したメソッドは次の順で呼ばれます。

  1. AssemblyInitialize (アセンブリの初期化)
  2. ClassInitialize (クラスの初期化)
    1. TestInitialize (TestMethod1の初期化)
    2. TestMethod1 (TestMethod1の実行)
    3. TestCleanup (TestMethod1の終了処理)
    1. TestInitialize (TestMethod2の初期化)
    2. TestMethod2 (TestMethod2の実行)
    3. TestCleanup (TestMethod2の終了処理)
  3. ClassCleanup (クラスの終了処理)
  4. AssemblyCleanup (アセンブリの終了処理)

AssemblyInitializeAttribute

[AssemblyInitialize]
public static void AssemblyInit(TestContext context)
{
}
AssemblyInitializeAttribute Class (Microsoft.VisualStudio.TestTools.UnitTesting) | Microsoft Docs

トラブル対処法

Visual Studioでのトラブル対処法

テストの実行を取り消すことができない

テストの終了後にSystem.InvalidOperationExceptionが発生し、「進行中のテストの実行がないので、テストの実行を取り消すことができません。(The cancelation of the test run is not possible as there is no test run which is in progress.)」と報告されることがあります。これはデバッグの設定で[マイ コードのみを有効にする]を有効にすることで解決できます。visual studio 2012 - Unit Test: The cancelation of the test run is not possible as there is no test run which is in progress - Stack Overflow

Formが表示されない

Visual Studioでテストをデバッグ実行したときにFormが表示されないときは、FormのShowInTaskbarをfalseに設定します。winforms - Displaying Windows Forms inside unit test methods - Stack Overflow

Form form = new Form();

if (System.Diagnostics.Debugger.IsAttached)
{
    form.ShowInTaskbar = false;
}

form.ShowDialog();
MSDN (Microsoft Developer Network) から検索