NUnit Converter
NUnit Converter
はじめに
NUnit Converterは,JUnit Converterを.NET用に移植したものです.NUnit V2.1.4で動作確認を行っています.
ダウンロード
バージョン 公開日 ダウンロード 説明 1.0.0 2004/07/04 NUnit.Extensions.Converter.zip -
チュートリアル
ここでは,JUnit Converter解説ページのサンプルコードがNUnit Converterでどんなふうに書けるのかを解説していきましょう.
サンプルプログラム - Book クラス
次のBookクラスは,一冊の本の情報を保持するエンティティクラスです.コンストラクタでISBNコードを引数にとります.
public class Book { public Book(String isbn) { ... } public String ISBN; public String Title; public String Author; public String Publisher; public String PageCount; public int Price; public String Size; }
このクラスのテストコードを書いてみましょう:
1: using NUnit.Framework; 2: using NUnit.Extensions.Converter; 3: 4: namespace NUnit.Extensions.Converter.Samples 5: { 6: [TestFixture] 7: public class BookTest 8: { 9: [Test] 10: public void AgileBook() 11: { 12: Converter converter = new DefaultConverter(); 13: converter = new BeanConverter(converter, typeof(Book), "ISBN", "Title", "Author"); 14: Book book = new Book("0-13-597444-5"); 15: object expected = new object[] { 16: "ISBN=0-13-597444-5", 17: "Title=Agile Software Development", 18: "Author=Robert C. Martin", 19: }; 20: converter.AssertEquals(expected, book); 21: } 22: } 23: }
まず,NUnit Converterのusing指定を行います(2行目).そして,テストメソッドの最初にConverterオブジェクトを作り(12行目〜13行目),converter.AssertEqualsを使ってBookオブジェクトを検証しています(20行目).
ここで,2つのConverterクラスが登場します.
クラス名 BookTestでの役割 本来の機能 DefaultConverter expectedオブジェクトの文字列化 object配列(ICollectionオブジェクト)等を文字列化 BeanConverter Bookオブジェクトの文字列化 特定クラスのプロパティ(またはフィールド)の値を使って文字列化
つまり,Decoratorパターンを使ってexpected(object配列)とBookオブジェクト両方を文字列に変換できるConverterオブジェクトを作る,というわけです.
以上をまとめると,NUnit Converterを使う手順は次のようになります.
- DefaultConverterオブジェクトを作成する
- 検証するオブジェクトに応じたConverterを作成し,Decoratorパターンでかぶせていく
- 実測値と期待値を計算する
- Converter.AssertEqualsメソッドで検証する
ここで,Converterの主なメソッドをまとめておきましょう.
メソッド 意味 AssertEquals
(object expected, object actual)expected, actualを文字列化した結果を比較する AssertEqualsIgnoreOrder
(object expected, object actual)expected, actualがを文字列化した結果を比較する.但しexpectedまたはactualがICollectionなら各要素を文字列化してソートする. AssertSame
(object expected, object actual)Assert.AreSameと同じ.但しエラーメッセージはexpected, actualを文字列化する. ToString(object o)
引数オブジェクトを文字列化した結果を返す Print(object o)
引数オブジェクトを文字列化した結果を出力する Println(object o)
引数オブジェクトを文字列化した結果を改行つきで出力する
Eファクトリクラス
NUnit ConverterにはJUnit Converterにない便利な機能が用意されています.それがEファクトリクラス(E: Expectedの略)です.このクラスを使えば,テストコードで期待値を簡単に作ることができます.
例えば,さきほどのテストメソッドを書き直してみましょう:
[Test] public void E_aメソッドを使う() { Converter converter = new DefaultConverter(); converter = new BeanConverter(converter, typeof(Book), "ISBN", "Title", "Author"); Book book = new Book("0-13-597444-5"); object expected = E.a("ISBN=0-13-597444-5", "Title=Agile Software Development", "Author=Robert C. Martin"); converter.AssertEquals(expected, book); }
メソッド:E.aは,object配列(正確にはICollectionオブジェクト)を返すファクトリメソッドです(a: arrayの略).object配列の期待値を作成するときに使います.
ところで,Converterライブラリでは上のコードにあるような「名前=値」のペアのことをPropertyと呼びます.実際にPropertyクラスが用意されており,ConverterはPropertyオブジェクトを「名前=値」の文字列として変換します.
本来なら,Propertyクラスを使って
object expected = E.a(new Property("ISBN", "0-13-597444-5"), new Property("Title", "Agile Software Development"), new Property("Author", "Robert C. Martin"));
と書くのですが,次のように書くこともできます:
object expected = E.a(E.p("ISBN", "0-13-597444-5"), E.p("Title", "Agile Software Development"), E.p("Author", "Robert C. Martin"));
メソッド:E.pは,Propertyオブジェクトを返すファクトリメソッドです(p: propertyの略).これでだいぶすっきりしました.
もともとEファクトリクラスはツリー構造になるような複雑なオブジェクトの期待値を書くために用意されました.例えば,
object expected = new Property("root", new object[] { new Property("sub1", new object[] { "sub1-1", "sub1-2", }), new Property("sub2", new object[] { new Property("sub2-1", new object[] { "sub2-1-1" }), }), "sub3", });
と書くのは大変ですが,Eファクトリクラスで次のように書けます:
object actual = E.ap("root", E.ap("sub1", "sub1-1", "sub1-2"), E.ap("sub2", E.ap("sub2-1", "sub2-1-1")), "sub3");
メソッド:E.apは,配列の値をもつPropertyオブジェクトを返すファクトリメソッドです(ap: array propertyの略).
以上をまとめると,次のようになります.
メソッド 生成するオブジェクト 名前の由来 E.a(element1, element2, ...)
配列(ICollection)オブジェクト array E.p(name, value)
Propertyオブジェクト property E.ap(name, element1, element2, ...)
配列(ICollection)の値を持つPropertyオブジェクト array property
展開レベルの設定
展開レベルはJUnit Converterとほぼ同じです.展開レベルを指定すれば,エラーメッセージをわかりやすく出力させることができます.
デフォルトでは,展開レベルが0のため,次のようなエラーメッセージが出力されます.
Failures: 1) NUnit.Extensions.Converter.Samples.BookTest.展開レベルなし : expected:<[ISBN=0-13-597444-5, Title=Agile Softare Development, Author=Robert C. Martin]> but was:<[ISBN=0-13-597444-5, Title=Agile Software Development, Author=Robert C. Martin]> at NUnit.Extensions.Converter.Converter.AssertEquals(Object expected, Object actual) at NUnit.Extensions.Converter.Samples.BookTest.展開レベルなし()
この場合,展開レベルを1にするとわかりやすくなるでしょう.
2) NUnit.Extensions.Converter.Samples.BookTest.展開レベルあり : expected:<[ ISBN=0-13-597444-5 Title=Agile Softare Development Author=Robert C. Martin ]> but was:<[ ISBN=0-13-597444-5 Title=Agile Software Development Author=Robert C. Martin ]> at NUnit.Extensions.Converter.Converter.AssertEquals(Object expected, Object actual) at NUnit.Extensions.Converter.Samples.BookTest.展開レベルあり()
展開レベルは,Converter.AssertEqualsメソッドの引数またはConverter.ExpansionLevelプロパティで指定できます.つまり,
converter.AssertEquals(expected, book, 1);
とするか
converter.ExpansionLevel = 1; ... converter.AssertEquals(expected, book);
とすることで,展開レベルが1になります.検証するオブジェクトが複雑な場合は展開レベルを上げてみるとよいでしょう.
BeanConverter
BeanConverterは,ある特定クラスのプロパティを調べるときに用いられます.機能はJUnit Converterの対応クラスとほぼ同じですが,各プロパティがgetアクセサであろうがpublicフィールドであろうが区別しないところが異なります.
例えば,BookクラスのAuthorプロパティのテストコードを書くとします.
converter = new BeanConverter(converter, typeof(Book), "Author");
最初の実装では,
public class Book { .... public string Author; .... }
のようにAuthorプロパティがpublicフィールドとして定義されていたとしても,後からリファクタリングでgetアクセサに変更できる,ということです:
public class Book { .... public string Author { get { ... } } .... }
つまり,C#言語はUniform Access Principleが守られているため,NUnit Converterもそれに対応したということになります.
BeanValueConverter
BeanValueConverterは,ある特定クラスをプロパティ値の配列で表現するときに用いられます.
[Test] public void BeanValueConverterを使う() { Converter converter = new DefaultConverter(); converter = new BeanValueConverter(converter, typeof(Book), "ISBN", "Title", "Author"); Book book = new Book("0-13-597444-5"); object expected = E.a("0-13-597444-5", "Agile Software Development", "Robert C. Martin"); converter.AssertEquals(expected, book); }
期待値の作成が楽なため,実際にはBeanValueConverterを使うことが多いでしょう.
BeanValueConverterは,JUnit Converterの対応クラスと同じでフォーマット指定ができます:
[Test] public void BeanValueConverterのフォーマット指定() { Converter converter = new DefaultConverter(); converter = new BeanValueConverter(converter, "{0}: {1}/{2}", typeof(Book), "ISBN", "Title", "Author"); Book book = new Book("0-13-597444-5"); object expected = "0-13-597444-5: Agile Software Development/Robert C. Martin"; converter.AssertEquals(expected, book); }
けれども,Eファクトリメソッドのおかげでこのフォーマット指定を使うことはほとんどなくなりました.
参考資料
ここまでの解説で,かなりのテストコードが書けるようになると思います.さらに詳しい内容が知りたい方は,以下を参考にしてください;
- JUnit 実践講座 - Converterを使ったJUnitプログラミング
JUnit Converterの解説ページです.Converterの自作方法(SpecificObjectConverter)やカスタマイズ方法(Evaluator)についても書かれています.これらはNUnit Converterでも実装されています.
- Cotton Bolls: NUnit Converter
C#特有のインデクサを用いたIndexerConverterクラス,SqlのSelect文からデータベースの内容を文字列化するSqlSelectConverterクラスについて解説しています.
更新履歴
- 公開 ― 2004/07/04