TDDでデータベースと付き合う方法

CodeZine / 2012年10月30日 14時0分

【参考】Visual Studio 2012 Ultimateで、CsTdd07最終形プロジェクトの各種メトリックスを見る

 業務アプリケーションの開発では、データベースと付き合わないわけにはいきません。しかし、テストファーストはやりにくいものです。今回は、データベースにアクセスするコードを、TDDでどうやって扱うのかをご紹介します。

■はじめに

 データベースを読み書きする部分のユニットテストがやりにくいのには、いくつか理由があります。

複数人でテストを同時に実行すると、競合する データベースを使ったテストは、時間が掛かる データベース内のデータが変わると、テストが失敗する  1番目は、各自の開発環境にテスト用のデータベースを用意することで、解決できます。2番目の問題は、データベースにアクセスするコードをロジックから分離して、データベースに実際にアクセスするテストケースを減らすことで、改善できます(ロジックのテストにはモックやダミーを使います)。3番目は、テストのたびにデータベースの内容を初期化することが基本になりますが、そうするとテストに長い時間が掛かるようになってしまいます。

 今回は、ビジネスロジックの開発時にモックやダミーを使いやすくするにはどうするか、また、テスト時にデータベースの内容を安定させるにはどうしたらよいかを、考えてみます。

■対象読者

TDDに興味をお持ちの.NET Frameworkの開発者。 ■必要な環境

 サンプルコードを試してみるには、C# 2010(Expressで可)またはVisual Studio 2012(Expressで可)、およびNUnit 2.6とSQL Server Compact 3.5が必要です。本稿執筆時点では、下記から入手できます。

開発環境の入手先
C# 2010 Express: Microsoft Visual Studio Express
Visual Studio Express 2012: Visual Studio Express 2012 for Windows Desktop NUnit 2.6: NUnit V2 2.6.1
NUnitのインストール手順: NUnit 2.5 の導入 Step by Step(筆者サイト、旧バージョンでの説明ですが基本的に同じです) SQL Server Compact 3.5: Microsoft SQL Server Compact 3.5 Service Pack 2 今回はSQL Serverが必要です。無償のExpressやCompact 4.0等で構いませんが、Northwindデータベースを使います。なお、サンプルコードではSQL Server Compact 3.5 SP2を使っていますので、接続文字列やクラス名などは適宜読み替えてください。



■とりあえずコードを作ってみる

 それでは、Customersテーブルから複数件のデータを取得してくるコードを、とりあえず書いてみましょう。今回は、ADO.NETを使うことにします。

 製品のロジックの中に、こんなメソッドを作ります。

【製品コードの仕様】名前の先頭一致で顧客を抽出する クラス名.メソッド名 顧客管理ロジック.前方一致で姓名を検索する()。 引数 string型。 返値 顧客情報クラスのコレクション型。Customersテーブルから、顧客のファーストネームまたはラストネームと引数を前方一致で比較して、一致したデータを返す。 Nothwindデータベースが初期状態のままなら、次のようなユニットテストを書くことができます。

テストコード: とりあえず書いてみた
[TestFixture] public class 顧客管理ロジックTest { [TestCase] //製品コード:空のリストを返す。Customerクラスは宣言だけ。 public void 前方一致で姓名を検索するTest_引数が空なら0件() { var head = ""; IList<Customer> customers = 顧客管理ロジック.前方一致で姓名を検索する(head); Assert.AreEqual(0, customers.Count()); } [TestCase] //製品コード:CustomerクラスとSQL文(パラメーター未使用)を実装 public void 前方一致で姓名を検索するTest_ファーストネームが一致() { var head = "Th"; var customers = 顧客管理ロジック.前方一致で姓名を検索する(head); var first = customers[0]; Assert.AreEqual("UK", first.Country); Assert.AreEqual("Around the Horn", first.CompanyName); Assert.AreEqual("AROUT", first.CustomerID); Assert.AreEqual("Thomas Hardy", first.ContactName); } [TestCase] //製品コード:SQL文を完成させ、パラメタライズドクエリーに修正 public void 前方一致で姓名を検索するTest_ラストネームが一致() { var head = "An"; var customers = 顧客管理ロジック.前方一致で姓名を検索する(head); var first = customers[0]; Assert.AreEqual("Germany", first.Country); Assert.AreEqual("Alfreds Futterkiste", first.CompanyName); Assert.AreEqual("ALFKI", first.CustomerID); Assert.AreEqual("Maria Anders", first.ContactName); } }
 このテストを通すように、製品コードを書きます(実際は、テストケースを1つずつ進めていきます)。

製品コード: とりあえず実装してみた
public class Customer { public string Country { get; set; } public string CompanyName { get; set; } public string CustomerID { get; set; } public string ContactName { get; set; } } public class 顧客管理ロジック { public static IList<Customer> 前方一致で姓名を検索する(string head) { var list = new List<Customer>(); if(string.IsNullOrWhiteSpace(head)) return list; //引数が空文字のときは、空のリストを返す。 // DB アクセス using(SqlCeConnection conn = new SqlCeConnection(@"Data Source=C:\Program Files (x86)\Microsoft SQL Server Compact Edition\v3.5\Samples\Northwind.sdf")){ conn.Open(); var cmdText = @"SELECT Country, [Company Name], [Customer ID], [Contact Name] FROM Customers WHERE ([Contact Name] LIKE @p1) OR ([Contact Name] LIKE @p2)"; using (DbCommand cmd = new SqlCeCommand(cmdText, conn)) { { var p1 = cmd.CreateParameter(); p1.ParameterName = "@p1"; p1.DbType = System.Data.DbType.String; p1.Value = head + "%"; //ファーストネームとの前方一致 cmd.Parameters.Add(p1); } //(変数p1のスコープをここで切っている) { var p2 = cmd.CreateParameter(); p2.ParameterName = "@p2"; p2.DbType = System.Data.DbType.String; p2.Value = "% " + head + "%"; //ラストネーム・ミドルネームとの前方一致 cmd.Parameters.Add(p2); } using (DbDataReader reader = cmd.ExecuteReader()) { //クエリー実行 while (reader.Read()) { //結果を一行ずつオブジェクトに詰め替え var customer = new Customer() { Country = reader.GetString(0), CompanyName = reader.GetString(1), CustomerID = reader.GetString(2), ContactName = reader.GetString(3), }; list.Add(customer); } } } } return list; } }


■関連記事
プログラムをマクロ(大域的)に分析する
TDDでデータベースと付き合う方法
データベースのデータからバブルチャートを作成する.NETアプリケーションを作る
ActiveReports+WebMatrix 2を使ってWindows Azureで簡単PDF出力
入力したデータがリアルタイムに反映されるバブルチャートを実装する

■記事全文へ

CodeZine

トピックスRSS

ランキング