xUnit.NETは.NET 2.0以上で動作するテストツールで、MicrosoftのBrad Wilson氏とJames Newkirk氏が中心となって開発を進めています。xUnit.NETは拡張性の向上、カスタム属性の減少、メソッドごと のインスタンス生成を特徴としており、Moq、Ninject、Oxite、KiGGなどのOSSにも採用されています。

Assertクラスのメソッド(抜粋)

前編では、xUnit.NETの導入方法、テストコード、Fact属性とTrait属性の設定を紹介しましたが、今回は後編として、Assertクラスの検証メソッド、データドリブンテストの実行、メソッドが呼び出される順番の確認について説明します。

Assertクラスには、さまざまな検証用のメソッドが用意されています。これらのメソッドを次の4種類に分けて見ていきましょう。

  • 基本的なメソッド
  • コレクション関連のメソッド
  • 例外の確認
  • 独自比較メソッド

Assertクラスの基本的なメソッドの確認

まずは、基本的な「True」「False」「Same」「Null」「IsType」「IsAssignableFrom」を利用するコードを見てみましょう。

[Fact]
public void 基本的なAssertメソッドの確認()
{
//2足す3
Calc c1 = new Calc();
int sum = c1.Add(2, 3);

//合計は5と等しいはず(True)
Assert.True(5 == sum);

//合計は5と異ならないはず(False)
Assert.False(5 != sum);

//c1とc2は同じインスタンスのはず(Same)
Calc c2 = c1;
Assert.Same(c1, c2);

//c3はヌルのはず(Null)
 Calc c3 = null;
Assert.Null(c3);

//c4はCalc型のはず(IsType)
Calc c4 = new Calc();
Assert.IsType<Calc>(c4);

//CalcSubClassはCalcに代入できるはず(IsAssignableFrom)
CalcSubClass cs = new CalcSubClass();
Assert.IsAssignableFrom<Calc>(cs);
}

基本的なアサーションの確認

メソッド名のテストが実行されるだけですので、それほど難しくはないでしょう。それぞれ「NotNull」「IsNotType」のように「Not」を付けることで逆の意味のテストを実行できます。

コレクションと範囲に関するメソッド

続けて、コレクションや範囲を比較する「Empty」「Single」「Contains」「InRange」のコードを見てみましょう。

[Fact]
public void コレクションに関するAssertメソッド()
{
//コレクションが空っぽのはず(Empty)
List<string> l1 = new List<string>();
Assert.Empty(l1);

//コレクションの要素は1つのはず(Single)
List<string> l2 = new List<string>();
l2.Add("要素を1つ追加");
Assert.Single(l2);

//コレクションに一致する要素が含まれているはず(Contains)
List<string> l3 = new List<string>(new string[] { "あいう", "かきく" });
Assert.Contains("あいう", l3);
Assert.DoesNotContain("いう", l3);

//文字列に特定の文字が含まれているはず(Contains)
string s1 = "あいう";
Assert.Contains("いう", s1);

//範囲の間であること(InRange)
int i = 4;
Assert.InRange(i, 3, 5);  //4は、3と5の間である
Assert.NotInRange(i, 1, 3); //4は、1と3の間ではない
}

コレクションと範囲をチェックするテストコード

EmptyメソッドとSingleメソッドはコレクションの要素数をチェックできます。Containsメソッドは、コレクションの中に一致する要素が存在するかをチェックできます。ただし、String型に対してContainsメソッドを実行した場合は、任意の文字列が含まれているかという意味になります。

例外のテスト

例外が発生することをテストする際は、Throwsメソッドを使用します。以下のコードでは、ゼロ除算例外が発生することをテストしています。

[Fact]
public void ゼロ除算でエラーが出るはず()
{
Calc c = new Calc();
Assert.Throws<DivideByZeroException>(
delegate
{
    //3割0はDivideByZeroException
    c.Divide(3, 0);
});
}

例外が発生することを確認するテスト

なお、基本クラスを指定するとテストに失敗しますので注意してください。例えば、Throws<DivideByZeroException>の部分をThrows<Exception>と記述するとテストに失敗します。

独自クラスを記述して比較を行う

これまで紹介したメソッドでは比較できない場合、独自の比較メソッドを使用することができます。例えば、空白を無視して文字列を比較するケースを見てみましょう。

//(1)空白を無視して文字列を比較するクラス
public class MyCompare : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
    return (x.Replace(" ", "") == y.Replace(" ", "")) ? true : false;
}

public int GetHashCode(string obj)
{
    return obj.GetHashCode();
}
}

[Fact]
public void 独自比較クラスの使用()
{
string s1 = "青木淳夫";
string s2 = "青 木 淳 夫";

//s1とs2は一致しないはず
Assert.NotEqual(s1, s2);

//(2)s1とs2は空白を無視すれば一致するはず(比較クラスを第3引数指定)
Assert.Equal(s1, s2, new MyCompare());
}

独自比較するクラスを使用してテストを実行

まず(1)の部分でIEqualityComparerインタフェース(System.Collections.Generic名前空間)を実装したMyCompareクラスを定義します。Equalsメソッドをオーバーライドして比較する方法を記述します。ここでは半角スペースを除外してから比較を行っています。次に(2)の部分でEqualメソッドの第3引数にMyCompareクラスを指定します。これによって、半角を無視してs1とs2の比較を行うことができます。

このように比較用のクラスを作成することで、頻繁に使用するチェックロジックを再利用することができます。