unit test pattern--1
まず、どのコードにunit testが必要かをお話しします.1.サードパーティインタフェースに依存するモジュール.サードパーティが提供する結果が完全に信頼できるとは限らないため、データが変化する可能性があります.サードパーティインタフェースのテストがあれば、プログラムに問題が発生した場合、unit testを走ってみると、サードパーティインタフェースのデータが変化しているのか、自分のコードに問題があるのかを判断することができます.これによりdebugの時間を短縮できます.2.モジュール間のインタフェース.上の理由から、モジュール間のインタフェースは、いくつかのモジュールを接続する唯一のチャネルです.インタフェースのテストでは、まず問題をモジュールに省略することもできます.
次に、ユニットテストで注意すべき点を説明します.1.サブユニットテストでは、以下の問題に遭遇したことがあるかどうかを考えてみましょう.ユニットテストに失敗しましたが、エラーの原因を特定するには時間がかかります.なぜかというと、unit testが担当する機能が大きすぎるからです.このとき、このunit testをいくつかの小さなtestに分けてテストする必要があります.私もこの問題に直面します.クラスはpublicのインタフェースを提供します.私はunit testを書いてこのインタフェースをテストします.しかし、このインタフェースは、A,B,Cのような3つの関数で実現される可能性がある.だからunit testが失敗したことに気づいたとき、A、B、Cのどの間違いが原因なのか分かりません.では、A、B、Cにunit testを追加してテストすると、エラーの場所を特定しやすくなります.もしここでAがprivateかもしれないならば、Aはunit testがこの関数のためにテストする必要がありますか?private(またはprotected)の関数がテストする必要があるかどうかの問題では、private関数をテストするべきではないという人もいます.private関数がprivateに設定されているのは、外部にアクセスしたくないし、privateの関数が変化しやすいため、unit testも変化しなければならないからです.private関数をテストする必要があるのは、サブ関数をテストしないと、エラーが位置決めしにくいという問題があるからだという人もいます.私にとって、場合によってはprivate関数をテストすることもあります.privateの場合、反射メカニズムでアクセスできます.protectedの関数に対して、私は一般的にunit testのclassにテストされたクラスを継承させ、unit testのclassでテストする関数にアクセスすることができます.2.Mock Object unit testを書いたことがある人は、ほとんどMock Objectを書いたことがあります.モックは真似の意味で、本当の相手ではないことを証明します.たとえば、データベースに依存する論理が正しいかどうかをテストしますが、実際にデータベースを操作すると、データベースのレコードに影響を与える可能性があります.あるいは、ファイルを操作し、ファイルを操作するにはIO異常を処理する必要があります.しかし、もしあなたが本当のファイルを読んでいたら、IO異常を処理するコードが正しいかどうかは永遠に分からないかもしれません.このとき、MockのオブジェクトでIOに異常が発生したことをシミュレートして、プログラムが正常に処理できるかどうかを見ることができます.たとえば、streamWriterからファイルにアクセスするclassを定義します.
unit testテストでStreamWriterが必要になると、MockStreamWriterのオブジェクトに転送されます.これにより、Writeメソッドを呼び出すと、MockStreamWriterのWriteメソッドが呼び出され、コードがhandle IOExceptionに成功するかどうかをテストできます.3.Self Shunt Self Shuntとは、classを追加することなくunit test自身をMock Objectとすることができるということです.例えば今class Personがあります.このclassにはListenerがたくさんあります.Personの中のNotifyが実行すれば、objectの状態をListenerに通知します.
通常のテストでは、Listenerから継承されるMock Listenerが必要です.
Unit Test:
Unit TestのclassにMockListenerとして機能を追加します.
このようなメリットは、追加のclassを書く必要がないことです.しかし、Listenerのすべてのインタフェースを実装する必要がある可能性があります(合理的な値を返すか、例外を投げ出すことができます).Unit Test自体をMock objectとすると、コードの読みやすくなります.
次に、ユニットテストで注意すべき点を説明します.1.サブユニットテストでは、以下の問題に遭遇したことがあるかどうかを考えてみましょう.ユニットテストに失敗しましたが、エラーの原因を特定するには時間がかかります.なぜかというと、unit testが担当する機能が大きすぎるからです.このとき、このunit testをいくつかの小さなtestに分けてテストする必要があります.私もこの問題に直面します.クラスはpublicのインタフェースを提供します.私はunit testを書いてこのインタフェースをテストします.しかし、このインタフェースは、A,B,Cのような3つの関数で実現される可能性がある.だからunit testが失敗したことに気づいたとき、A、B、Cのどの間違いが原因なのか分かりません.では、A、B、Cにunit testを追加してテストすると、エラーの場所を特定しやすくなります.もしここでAがprivateかもしれないならば、Aはunit testがこの関数のためにテストする必要がありますか?private(またはprotected)の関数がテストする必要があるかどうかの問題では、private関数をテストするべきではないという人もいます.private関数がprivateに設定されているのは、外部にアクセスしたくないし、privateの関数が変化しやすいため、unit testも変化しなければならないからです.private関数をテストする必要があるのは、サブ関数をテストしないと、エラーが位置決めしにくいという問題があるからだという人もいます.私にとって、場合によってはprivate関数をテストすることもあります.privateの場合、反射メカニズムでアクセスできます.protectedの関数に対して、私は一般的にunit testのclassにテストされたクラスを継承させ、unit testのclassでテストする関数にアクセスすることができます.2.Mock Object unit testを書いたことがある人は、ほとんどMock Objectを書いたことがあります.モックは真似の意味で、本当の相手ではないことを証明します.たとえば、データベースに依存する論理が正しいかどうかをテストしますが、実際にデータベースを操作すると、データベースのレコードに影響を与える可能性があります.あるいは、ファイルを操作し、ファイルを操作するにはIO異常を処理する必要があります.しかし、もしあなたが本当のファイルを読んでいたら、IO異常を処理するコードが正しいかどうかは永遠に分からないかもしれません.このとき、MockのオブジェクトでIOに異常が発生したことをシミュレートして、プログラムが正常に処理できるかどうかを見ることができます.たとえば、streamWriterからファイルにアクセスするclassを定義します.
class MockStreamWriter : StreamWriter
{
public override void Write(string value)
{
throw new IOException("");
}
}
unit testテストでStreamWriterが必要になると、MockStreamWriterのオブジェクトに転送されます.これにより、Writeメソッドを呼び出すと、MockStreamWriterのWriteメソッドが呼び出され、コードがhandle IOExceptionに成功するかどうかをテストできます.3.Self Shunt Self Shuntとは、classを追加することなくunit test自身をMock Objectとすることができるということです.例えば今class Personがあります.このclassにはListenerがたくさんあります.Personの中のNotifyが実行すれば、objectの状態をListenerに通知します.
class Person
{
//other methods and fields....
public IList ListenerList;
public void Notify()
{
for (int i=0; ithis);
}
}
}
通常のテストでは、Listenerから継承されるMock Listenerが必要です.
class MockListener : Listener
{
public int Count = 0;
public override void Accept(Person p)
{
Count++;
}
}
Unit Test:
public void Test()
{
Person p = new Person();
MockListener listener = new MockListener();
p.ListenerList.Add(listener).
p.Notify();
Assert.AreEqual(1, listener.Count);
}
Unit TestのclassにMockListenerとして機能を追加します.
class MockListener_UnitTest : Listener
{
public int Count = 0;
public override void Accept(Person p)
{
Count++;
}
public void Test()
{
Person p = new Person();
p.ListenerList.Add(this).
p.Notify();
Assert.AreEqual(1, this.Count);
}
}
このようなメリットは、追加のclassを書く必要がないことです.しかし、Listenerのすべてのインタフェースを実装する必要がある可能性があります(合理的な値を返すか、例外を投げ出すことができます).Unit Test自体をMock objectとすると、コードの読みやすくなります.