コードの再確認
エンタープライズ サービスを使用したデータ層単体テストの簡略化
Roy Osherove
翻訳元: Simplify Data Layer Unit Testing using Enterprise Services (英語)
この記事で取り上げる話題:
- 単体テストの原則
- データベースの状態管理
- エンタープライズ サービスと COM+ 1.5 の基本
- Services Without Components (SWC)
この記事で使用する技術:
- COM+ 1.5、C#、Visual Studio、Testing
目次
- データベース テストの状態
- 一貫性のある状態の維持
- データベースの復元
- ADO.NET トランザクション オブジェクト
- COM+ トランザクション ServicedComponent の継承
- 簡単で容易、かつ快適
- COM+ 1.5
- Visual Studio 2005 Team System での単体テスト
- まとめ
- 補足記事: テスト フレームワークの選択肢
単体テストやテスト主導型開発 (TDD) をめぐって、さまざまに大げさな宣伝がされているにもかかわらず、多くの開発者たちは、これらのテクニックを現実のアプリケーション開発に適用できる有用なプロセスとは考えていません。その理由の 1 つは、アプリケーション開発の複雑さに実際に取り組み始めると、すばらしいと聞かされてきた簡便なテスト手法が、たちまちすべて色褪せてしまうことにあります。そして結局、実際のコードに対して実装が困難な単体テストを作成する羽目になるわけです。これはデータベースのテストに多く当てはまります。データベースのテストでは、TDD はたちまち魅力を失い、開発プロセスを仕方なしに以前に行っていた現実的な方法に戻すことになります。この記事では、単体テストを使用して、現実のアプリケーションを開発する場合でも、テスト プロセスをより簡単なものにすることのできる、いくつかのテクニックについて説明します。
1. データベース テストの状態
優れた設計のデータドリブン システムは、ほとんどの場合、何らかのデータ アクセス層 (DAL) を備えています。これは、たとえば特定のクラスであったり、特定のデータベースとの通信をすべて担当する独立のクラス ライブラリ プロジェクトであったりします。この記事でデータベース単体テストという場合は、そのような特定の DAL コンポーネントの開発を試験および遂行する単体テストの作成を意味しています。
DAL のテストは、簡単なロジックやアルゴリズムのコードをチェックするよりも困難です。DAL に対して単体テストを実行するには、可能な方法が 2 つあります。第 1 の方法は、その DAL と通信するデータベース API を、データベースのインターフェイスを模擬するが実際には何のタスクも実行しない、独自に作成したオブジェクトと置き換える、というものです。この方法は、しばしば "モッキング" または "スタビング" と呼ばれます。通常、オブジェクトのモックは、特定の動作を実行し、置換されたオブジェクトで実行されるさまざまなアクションを予期しますが、スタブの方は、論理的なアクションのテストには使用されない何らかのものを置き換える、何の動作もしない単なる代替物です。スタブは、テスト対象のオブジェクトを、コストの大きい、または時間を消費するリソースとの相互作用から切り離すために役立ちます。モッキングは通常、オブジェクト間の相互作用をテストする、相互作用テストを行う際に使用します。モック オブジェクト (模擬オブジェクト) の詳細については、MSDN Magazine の 2004 年 10 月号に掲載された Mark Seemann の記事「Unit Testing: Mock Objects to the Rescue! Test Your .NET Code with NMock」(英語) を参照してください。
図 1 状態ベースのテスト第 2 のテスト方法では、バックグラウンドで実際のデータベースを呼び出す DAL を使用します。この場合、さまざまな問題が発生する可能性があります。この方法は、状態ベースのテストを行う際に使用します。オブジェクト間 (つまり DAL と ADO.NET インフラストラクチャの間) の論理呼び出しはテストせず、単にアクションを実行した後でテスト対象オブジェクトの状態をチェックすることにより、オブジェクトが要件を満たしているかどうかを確認します。図 1 に、すべてのオブジェクトと層がテストにどのように関与するかを示します。状態ベースのテストの詳細については、Martin Fowler の記事「Mocks Aren't Stubs」 (英語) を参照してください。
.gif)
図 1 状態ベースのテスト
ページのトップへ
2. 一貫性のある状態の維持
単体テストを作成する際には、注意すべき点が数多くあります。
テストの前には初期状態を必ず確認する
テストは、必ず予測可能な初期状態から開始する必要があります。たとえば、特定のデータベース テーブルから行を削除する機能をテストする場合には、テストを開始する前に、テーブル内でテスト中に削除してもよいデータを必ず確認しておく必要があります。つまり、毎回テストを開始する前には、それがデータベース内のデータであれ、テスト対象オブジェクトの新しいインスタンスであれ、内容のわかっている初期状態を設定する必要があります。これを行うには、現在実行中のテスト以外に手段はありません。
これは通常、セットアップ メソッドの助けを借りて行います。セットアップ メソッドとは、各テスト ケースごとに呼び出されるテスト フィクスチャ内のコンストラクタのようなものです。たとえばテスト ケースが 5 つある場合、セットアップ メソッドは、各テスト ケースが開始される前に 1 回ずつ、全部で 5 回起動されます。NUnit を使用する場合、開始メソッドは、任意のメソッドに SetUpAttribute でマーク付けすることで作成します。これは、テスト対象のオブジェクトの新しいインスタンスをセットアップする場所であり、それによって各テストの状態を、他のすべてのテストの状態から切り離します。
各単体テストは、他のテストから独立させる
テストは、そのジョブを実行するために、他の単体テストに依存してはいけません。また、テストを実行するために、特定の順序を必要としてはいけません。つまり、それぞれの単体テストを個別に、任意の順序で実行できる必要があり、また、実行するたびに同じ結果が得られなければなりません。NUnit フレームワークは、テストが特定の順序で実行されることを保証していません。そのため、それぞれのテストを、テスト プロジェクト全体で唯一のテストであるかのように作成する必要があります。
単体テストは、一貫性のある、予測可能な仕方で失敗または成功する
有効なテストが失敗したら、そのテストは、失敗の原因となったバグを修正するまで失敗し続けなければなりません。同様に、テストがいったん成功したら、新たな失敗の原因となる変更をシステムに加えるまで、そのテストは成功し続ける必要があります。同じコンパイル済みコードに単体テストを複数回実行して、ある時には失敗し、ある時には成功する、ということになると、間違って安全であると思い込んだり、むやみに失望したりすることになります。繰り返しますが、テストの前には初期状態を必ず確認してください。
稼働中のデータベースに対してテストを実行する場合の問題点は、作成した数多くの単体テストがデータベースの状態を変化させ、この変更が同じデータベースに対して実行される他のすべての単体テストに影響を与えることです。たとえば、データベースからある行を削除するテストがあるとします。もし、そのテストとは別に、実行前にデータベース内に特定の数の行が存在することを前提するテスト (たとえば、返された行数を調べて、読み取り操作が成功しているかどうかをチェックするテスト) があったとすると、このテストを削除テストの直後に実行すると失敗します。データベースは他の開発者やテスト担当者と共用である可能性があるため、セットアップ メソッドを使ってテスト状態に新しいオブジェクトを追加する、というわけにはいきません。この状態の外部依存性は、次のテストを実行する前に、何らかの方法で解決しておく必要があります。この場合には、新しいオブジェクト インスタンスを作成しても解決にはなりません。データベースを実際に元の状態に戻す必要があるからです。
これまでに説明したすべてのルールで、テスト間でデータベース状態を一貫させることの重要性を強調してきました。各テストでデータベースに対して行ったすべての変更は、ロール バックという処理こそが最も問題が多く、コードが保守できなくなる可能性があるとしても、元に戻す必要があります。
これまでに説明した問題には、いくつかの解決策があります。この記事は、特定の解決策を詳細に説明することを目的としているため、他の解決策については扱いません。さいわいなことに、それらの解決策は非常に簡単です。
ページのトップへ
3. データベースの復元
データベースを既知の状態に復元するための完全な方法は、以前に作成したバックアップ コピーから実際に復元することです。各テストの前に簡単な RESTORE DATABASE スクリプトを実行することで、テストを毎回必ず同じデータベースに対して実行できるようになります。しかし、これには非常に時間がかかります。小規模な SQL Server データベースの簡単な復元に 5 秒かかるとし、たとえば 200 回のテストごとにその復元を繰り返す (テストごとに、データベースを復元するセットアップ メソッドを起動する) とすると、復元処理だけで 16 分以上のオーバーヘッドがこれらのテスト全体に追加されることになります。この方法では、特にコードを作成しながらテストを実行する必要がある場合には、丸 1 日かかってしまいます。しかし、この解決策は、データベースに大量のデータを書き込むテスト (たいていは負荷テスト) を実行する場合のような、特定のケースには役立ちます。この記事で説明している単体テストは、このようなテストよりもはるかに単純なため (各テストでは、データベースに対して 1 行~数行の挿入/更新/削除だけを実行します)、通常このようなケースには該当しません。
同じテスト プロジェクトで複数のデータベースに対してテストを実行すると、事態は少々複雑になり、パフォーマンスは低下し始めます (復元に要する時間がデータベースの数だけ倍加され、大変な数字になります)。多数の開発者が同じデータベースに対してテストを実行しても、それぞれの開発者のテストが他のすべての開発者のテストを妨げるため、同じことが当てはまります。
各テストごとにデータベースを完全には復元しないようにする場合は、データベース操作に対応するトランザクションを使用して、変更をロール バックすることができます。これを実現するには、いくつかの方法があります。
ページのトップへ
4. ADO.NET トランザクション オブジェクト
トランザクションは、この問題を解決するための優れた方法です。James Newkirk は、彼の著書『Test Driven Development with Microsoft .NET』 (Microsoft Press 刊、2004 年) で、単体テストがデータベースに与えた影響を、トランザクションを使用して元に戻す方法について述べています。ただし、彼が説明しているのは、ADO.NET 1.x のトランザクションを使用してこれを行う方法であるため、私の意見では、最もクリーンな方法とは言えません。考え方としては、テストのセットアップ メソッド内でトランザクション オブジェクトを明示的に開き、次にその ITransaction オブジェクトを、開かれている接続を使用してタスクを実行するテスト対象のオブジェクトに送信する、というものです。テストの最後には、ITransaction の Abort メソッドを呼び出すだけで、テスト中にデータベースに対して加えられた変更を元に戻すことができます。
この方法はあまりに複雑で、個々のプロジェクトごとに実装および保守することが非常に困難です。また、そのようなトランザクションを受信するために、テスト対象オブジェクトの API を変更する必要もあります。これは、一部のシステムでは実行可能かも知れませんが、API が閉じたままである必要がある、他の多くのシステムでは不可能です。このことは特に、データベースの機能を実行するために COM+ トランザクションを使用するシステムに当てはまります。Newkirk は、これが最もエレガントな解決策というわけではない、と認めていますが、私もそれに同意します。私は、テストの影響をロール バックできるようにするだけのために、私が作成したすべてのプログラムの API を変更したいとは思いません。もちろん、メソッドに情報を渡す方法には、メソッド署名の他にも、静的フィールドを使用する方法や、スレッドローカル ストレージ (TLS) を使用する方法などがあります。ADO.NET トランザクションをテストのために使用する場合は、代替策としてこれらの方法を考慮することもできます。
ページのトップへ
5. COM+ トランザクション ServicedComponent の継承
COM+ トランザクションは、上に述べたアプローチに伴う障害を克服するための優れた方法です。この場合、何らかの方法で単体テストを COM+ トランザクション内で実行されるようにし、テストの切断メソッド内でそのトランザクションを中止するようにします (切断メソッドは、テストの最後に実行され、NUnit では TeardownAttribute によって指定されます)。さらに、テスト対象の API を変更せずに済みます。そのままで正しく動作するはずです。意外なことに、これは実装が最も容易なものの 1 つです。
トランザクションに参加するには、System.EnterpriseServices.ServicedComponent クラスから継承したクラスが必要です。DAL オブジェクトをテストするテスト フィクスチャ クラスは、申し分のない候補者です。そのクラスに ServicedComponent を継承させ、それによって、System.EnterpriseServices 名前空間にある ContextUtil クラスを使用して COM+ トランザクションに参加できるようにします。COM+ の ServicedComponent を使用する場合に便利な点は、トランザクションを有効にするために、クラス宣言の一番上に単純な属性を指定すれば済むことです。この場合には、次の例に示すように、このクラスのインスタンスで呼び出される各パブリック メソッドが新しいトランザクション コンテキスト内で実行される必要があることを COM+ に通知する属性を指定できます。
[TestFixture]
[Transaction(TransactionOption.RequiresNew)]
public class TransactionalTests:ServicedComponent
{
[Test]
public void Insert()
{
//データベースに何かを挿入
}
}
このテスト フィクスチャを実行するたびに、各テストはトランザクション内で起動されます。 次に、各テストが実行された後に、それが参加したトランザクションがロール バックされるようにする必要があります。これは、図 2 に示すように、ContextUtil.SetAbort を使用してトランザクションをロール バックする、[TearDown] メソッドを使用すれば簡単に実行できます。
図 2 トランザクションのロール バック
[TestFixture]
[Transaction(TransactionOption.RequiresNew)]
public class TransactionalTests:ServicedComponent
{
[Test]
public void Insert()
{
// データベースに対して操作を実行する
CategoriesManager mgr = new CategoriesManager();
int newID = mgr.InsertCategory("MyCategory");
Assert.IsTrue(newID != 0, "returned ID should be more than zero");
}
[TearDown]
public void Teardown()
{
if(ContextUtil.IsInTransaction)
{
// 実行中のトランザクションを中止する
ContextUtil.SetAbort();
}
}
}
呼び出される DAL コードを含めたすべてのコード、およびこのコードが実行されたことによる、データベースに対するすべての変更がロール バックされます。図 3 に、COM+ トランザクションをロールバック メカニズムとして使用したテストのフローを示します。
.gif)
図 3 COM+ トランザクションを使用した変更のロール バック
ページのトップへ
6. 簡単で容易、かつ快適
この方法を使用する場合は、注意すべきいくつかの欠点があります。この仕方でサービス コンポーネントを使用すると、テスト フィクスチャ内のすべてのテストはトランザクション内で実行されることになるため、読み取り操作だけを実行し、データベースに対して何の変更も行わないテストでも、トランザクションが使用されます。そしてトランザクションは、データベースを完全に復元する場合ほど時間がかからないとはいえ、やはりコストを生じます。このオーバーヘッドを回避しようとすると、"読み取り" テストから "書き込み" テストを分離するためだけに、まったく新しいテスト フィクスチャを作成する必要があり、それによって単体テストのコードがより複雑になります。
テストしているコードに、COM+ のトランザクション要件と競合する特別な要件がある場合には、ロールバックは利用できないものと考える必要があります。この競合は、いくつかの一般的なシナリオで発生します。第 1 のシナリオは、DAL オブジェクト内に、それ自身で TransactionOption.RequiresNew を使用するコードが存在する、というものです。この場合 DAL オブジェクトは、テスト フィクスチャが開始したトランザクションを無視して、テスト内からは制御できない新しいトランザクションを作成します。第 2 のシナリオは、DAL オブジェクト内に、TransactionOption.Disabled または TransactionOption.NotSupported を使用するコードが存在する、というものです。この場合、コードはそのアクションを所定のトランザクション コンテキストの外部で実行するため、データベースに対する変更は、トランザクション コンテキストを使用してロール バックすることはできません。
ServicedComponent から継承したクラスは、厳密な名前のアセンブリの内部に配置する必要があります。したがって、テストは署名されている必要があります。また、厳密な名前のアセンブリから参照されるすべてのアセンブリは、同じく厳密に名前が指定されている必要があります。単純な概念実証アセンブリに対して簡単なデータベース テストを実行するだけの場合には、アセンブリに署名したり、名前を指定したりする時間がなかったり、面倒と感じることがしばしばあります。さらに、厳密な名前でないアセンブリをテストするには、最初にそのアセンブリを逆アセンブルしてから、厳密な名前のキーを使用して再アセンブルする以外に方法がありません。
ちなみに、NUnit の現在のバージョン (バージョン 2.2.1) は、理由はわかりませんが、サービス コンポーネントと組み合わせると正しく動作せず、GUI ドライバの内部でテストを実行すると、さまざまな例外をスローすることがあるようです。NUnit をテスト ハーネスとして使用している人にとっては、この問題が原因で、多くの場合にこの方法が実際上使用できなくなります (マシンや環境によってはうまく行く場合があるようです)。
ページのトップへ
7. COM+ 1.5
それでは、テストの実行に NUnit の GUI 実行モジュールを使用できない問題や、アセンブリに厳密な名前を指定しなければならない問題を克服するには、どのようにすればよいでしょうか。それには、COM+ 1.5 を導入します。COM+ 1.5 は、よく知られており、よく使用されている COM+ エンタープライズ サービスの機能拡張版です。この記事でこれから説明する機能を利用するには、Windows XP Service Pack 2 または Windows Server 2003 以降を実行している必要があります。このケースで COM+ 1.5 を必要とするマシンは、ユーザー用マシンではなく、テスト用/ビルド用/開発用マシンだけであるため、構成の問題を引き起こす可能性はまったくありません。上記のバージョンより古いバージョンのソフトウェアを実行しているシステムでこれらの機能を利用しようとすると、PlatformNotSupportedException がスローされます。
COM+ 1.5 では、Services Without Components (SWC) という機能により、ServicedComponent を明示的に継承しなくても、エンタープライズ サービスを使用できます。その際に必要なことは、System.EnterpriseServices 名前空間にある ServiceConfig および ServiceDomain という 2 つの非常に単純なクラスに習熟することだけです。これら 2 つのクラスを使用することで、継承のチェーンを変更する (つまり、ServicedComponent から継承したクラスを作成する) 必要なしに、トランザクションに参加したり、トランザクションを中止したりできます。図 4 に、前のサンプルのコードと同じ内容のテストをトランザクション内で実行する、簡単なコードを示します。
図 4 COM+ 1.5 のトランザクション コード
[SetUp]
public void Setup()
{
// ServicedComponent から継承せずに新しいトランザクションに入る
Console.WriteLine("Attempting to enter a transactional context...");
ServiceConfig config = new ServiceConfig();
config.Transaction= TransactionOption.RequiresNew;
ServiceDomain.Enter(config);
Console.WriteLine("Attempt suceeded!");
}
[Test]
public void Insert()
{
// データベースに対して操作を実行する
CategoriesManager mgr = new CategoriesManager();
int newID = mgr.InsertCategory("MyCategory");
Assert.IsTrue(newID != 0, "returned ID should be more than zero");
}
[TearDown]
public void Teardown()
{
Console.WriteLine("Attempting to Leave transactional context...");
if(ContextUtil.IsInTransaction)
{
// 実行中のトランザクションを中止する
ContextUtil.SetAbort();
}
ServiceDomain.Leave();
Console.WriteLine("Left context!");
// ContextUtil にアクセスを試行すると、今度は例外が発生する
}
ServicedComponent は使用されていません。その代わりに、COM+ 1.5 に導入された 2 つのクラスを非常に単純な仕方で使用しています。ServiceDomain は、トランザクション コンテキストに入ったり、出たりするために使用されています。トランザクション コンテキスト内では、いつでも ContextUtil オブジェクトを使用して、トランザクションを操作することができます。静的 Enter メソッドは、ServiceConfig 型の引数を 1 つ取ります。この引数は、トランザクション コンテキストにコンポーネントの要件を通知します。実のところ、これは前の例でクラスの一番上に配置した [Transaction] 属性の完全な代替物です。新しいトランザクションを必要とすることをコンテキストに通知する、ServiceConfig クラスのプロパティを指定します。すばらしいことに、これら 2 つの呼び出しの間で現在のトランザクションをコミットまたはロールバックできるコードを使用すれば、ServiceDomain にはいつでも入ったり出たりすることができます。
トランザクションに参加するテストと参加しないテストを指定することができます。この例では、各テストごとに SetUp と TearDown が呼び出されるため、すべてのテストが参加します。しかし、トランザクション ドメインに出入りするには時間がかかるため、このロールバック機能を必要としないテストが実際にはトランザクション内で作業をしない場合には、その方がよいでしょう。それに対し、各テスト ケースに個々の呼び出しを配置し、トランザクション ドメインを開いたり閉じたりすると、より反復的にコードを記述する必要が生じますが、粒度は向上します。ロールバック機能を持つ組み込み属性を提供する XtUnit や MbUnit などのフレームワークに関する詳細については、「テスト フレームワークの選択肢」サイドバーを参照してください。
テストでデータベースに対して大量の書き込みを行うと、ロールバックを実行することになり、トランザクションへの参加に時間がかかるようになります。そのような場合には、データベース復元を使用する方が高速で、望ましいと言えるでしょう。
SWC は、これまでに説明したトランザクション ベースの 3 種類の方法の中で最もエレガントで、最も柔軟性に富んでいます。.NET Framework 2.0 の新しい System.Transactions 名前空間は、私の奥の手として取っておくことになりますが、この方法は今のところ、データベースとの相互作用を伴う単体テストを実行する際の、私の最もお気に入りの方法です (System.Transactions の詳細については、MSDN Magazine の 2005 年 2 月号に掲載された John Papa の記事「Data Points: ADO.NET and System.Transactions」(英語) を参照してください)。
ページのトップへ
8. Visual Studio 2005 Team System での単体テスト
Visual Studio 2005 Team System は、新しい種類のプロジェクト タイプであるテスト プロジェクトを通じて利用できる、マネージ コードのテスト用の組み込み単体テスト フレームワークを提供します。テスト プロジェクトは、アプリケーションを試験して結果を返すために、単体テスト、負荷テスト、およびその他さまざまな種類のテストを作成できる、特別なプロジェクトです。テスト プロジェクトを含んだすべてのソリューションには、テスト実行構成ファイルも含まれています。このファイルには、テストを実行するための設定が保存されています。これらの設定には、テストが実行されるコンピュータ (ローカルまたはリモート)、ホストするプロセス (たとえば ASP.NET)、およびコード カバレッジの結果を収集するかどうかが含まれています。Team System では、テストをコマンド ラインと、Visual Studio IDE の両方から実行できます。実行後には結果と、失敗したテストがあれば失敗の理由が表示されます。結果は、他のチーム メンバが分析したり、レポートに組み入れたりできるように発行することができます。
単体テストを実行した後には、単体テストのコード カバレッジ (実行したテストによって試験されたコードの割合) が表示されます。コード カバレッジは、実際に実行されたコードのパーセンテージに関する統計と、Visual Studio に表示されたソースでのコードの色分けという 2 つの方法でレポートされます。実行されたコードは緑で強調表示され、実行されていないコードは赤で強調表示されます。
Team System では、データ駆動単体テストと、ASP.NET 単体テストという 2 つの特別な種類の単体テストも利用できます。データ駆動テストは、データ ソースのデータ行ごとに繰り返し呼び出されるように構成した単体テストです。ASP.NET 単体テストは、ページ要求に応答する ASP.NET アプリケーション内のコードを試験するために使用します。ASP.NET 単体テストは、テスト対象の ASP.NET アプリケーションのプロセス内でホストされます。
Team System のいくつかの新しい属性は、これまでの他の単体テスト ハーネスで使用したものと同様です。NUnit の TestFixtureAttribute の代わりには TestClassAttribute があり、NUnit の TestAttribute の代わりには TestMethodAttribute があります。SetUpAttribute および TearDownAttribute についても同様で、これらはそれぞれ TestInitializeAttribute と TestCleanUpAttribute によって置き換えられます。
Team System では、テスト対象のソース コードで使用されているクラスから、単体テストを自動的に生成することができます。生成された単体テストには最小限のコードが含まれており、意味のあるテスト結果を得るには、事前に編集しておく必要があります。単体テストを編集するには、Team System の単体テスト フレームワークのクラスやメソッドが助けになります。たとえば、新しい種類の Assert メソッドが提供されます。これは、予想と異なる結果を特定するために使用できます。Assert クラスは、NUnit を使用したことのある人であれば、よく知っているはずです。
残念ながら、現在 Visual Studio 2005 にはデータベース単体テストを向上させるための特別な機能がありません。そのため、結局のところ、この記事で説明したいくつかのタスクを実行することになります。図 5 に、以前のデモとまったく同じロールバック メカニズムを使用した、SWC による簡単なテスト クラスを示します。構文は、少しだけ異なっています。 Visual Studio 2005 のすべての新機能と同様に、最終リリースの前に変更される可能性があります。
図 5 SWC によるロール バック
[TestClass]
public class TransactionalTests2005
{
[TestInitialize]
public void Initialize()
{
ServiceConfig config = new ServiceConfig();
config.Transaction = TransactionOption.RequiresNew;
ServiceDomain.Enter(config);
}
[TestMethod]
public void UpdateCategoriesTest()
{
// データベースに何かを挿入
}
[TestCleanup]
public void Cleanup()
{
if (ContextUtil.IsInTransaction)
{
ContextUtil.SetAbort();
}
ServiceDomain.Leave();
}
}
ページのトップへ
9. まとめ
データベースのテストは、正しいツールとテクニックを使用しなければ、困難なものになる可能性があります。データベース単体テストは、テスト プロセスの不可欠な一部です。COM+ 1.5 を使用することで、コードを期待通りに動作させることが非常に容易になります。Visual Studio の次のリリースでも、この記事で説明したテクニックの多くが、そのまま利用できるでしょう。Visual Studio 2005 では、テストで作成したコードを必ずしも変更する必要はありませんが、アプリケーションの単体テスト プロセスを短期間で立ち上げるために役立ちます。
ページのトップへ
10. 補足記事: テスト フレームワークの選択肢
単体テスト フレームワーク で、NUnit フレームワークの代わりとなり、RollbackAttribute という単純な属性を使用してトランザクションによる単体テストを実行できるものには、いくつかの選択肢があります。これらのフレームワークでは、トランザクションによる単体テストを制御するために COM+ 1.5 を使用しているため、すでに説明したものと同じオペレーティング システムが必要になります。
MbUnit
MbUnit は、テスト フレームワークの世界に新しく加わった優れたフレームワークです。これは Jonathan de Halleux によって開発され、mbunit.tigris.org (英語) でダウンロードできます。このフレームワークには、この記事で説明したものと同じテクニックを使用する RollbackAttribute が含まれています。
また、この記事の最初の方法で説明した、テストごとにデータベースの復元アクションを実行する SqlRestoreAttribute も含まれています。MbUnit には、最小または最大のパフォーマンス カウンタ値でテストを実行できる RepeatAttribute や PerfCounterAttribute などの、テスト開発者の作業を容易にする他のさまざまな属性も付属しています。MbUnit は、まだ発展途上ですが、NUnit のテストを実行する機能を含め、さまざまな利点を備えています。
XtUnit
XtUnit は、ダウンロードして、独自に作成したコード内で使用できるアドオン ライブラリです。これは、どのテスト フレームワークを使用している場合でも、テストにロールバック機能を追加するためのエレガントな方法です。これを実現するために、.NET のインターセプトを使用しています。これは拡張が可能なため、テストで実行するための独自の属性を容易に作成できます。このフレームワークの詳細については、teamagile.com/mainpages/downloads.html (英語) を参照してください。
NunitX
NunitX は、NUnit テスト フレームワークのバリアントで、GUI とコンソールの実行モジュールが含まれており、RollbackAttribute という新しい属性を組み込むために、わずかに拡張されています。この属性を特定の単体テストに適用すると、テストはトランザクション コンテキスト内で実行されるようになります。このフレームワークでは、COM+ 1.5 SWC を使用しています。この記事で説明したものと同じ原則が、このフレームワークにも実装されています。これは、www.osherove.com (英語) からダウンロードできます。
独自の属性の作成
NUnit フレームワークの最新バージョン (2.2.1) には、属性を追加することで拡張できる機能があります。この機能により、テストのために独自のロールバック属性を非常に簡単に追加できます。
Roy Osherove は、アジャイル ソフトウェア開発と .NET アーキテクチャに関するコンサルタント業務を行っている Team Agile の代表者です。また、関連情報を含んだブログ http://weblogs.asp.net/rosherove/ (英語) も開設しています。Roy の連絡先は、Roy@TeamAgile.com (英語) です。
この記事は、MSDN マガジン - 2005 年 6月号からの翻訳です。