連載
NAgileで始める実践アジャイル開発

第3回 ソフトウェアの良い設計を行うコツ

NAgiler 黒石 高広 & 小井土 亨
2006/02/25
Page1 Page2 Page3

4. データアクセス層でのテストファースト

 データアクセス層は、データベースやファイルシステムなどのデータ・ストアに対してアクセスを行うクラス群のことだ。データアクセス層でのテストファーストのツールは特に存在しないので、データアクセス技術を組み合わせて行う必要がある。

 そこで今回は、SQL Server 2005の新機能であるファイル・アタッチ機能と.NET Framework 2.0の新機能である自動トランザクションを利用したデータアクセス層でのテストファーストの実現方法を紹介する。

 なお先ほどのNUnitFormsの説明では.NET Framework 1.1を使用したが、以降では.NET Framework 2.0が必要だ。従って以降では、Visual Studio 2005 Professional Edition日本語版を使って説明する。

●ファイル・アタッチ機能の利用

ここでいうファイル・アタッチ機能とは、SQL Server 2005 Express Editionのみの新機能で、必要なときだけデータベース・ファイル(.MDFファイル)をSQL Serverに直接アタッチ(=接続)し、データベースとして利用する機能である(以前は、このような自動的なアタッチ/デタッチはできなかった)。このファイル・アタッチ機能を利用することで、開発クライアントへのテスト用データベースの配布を容易に行える。ファイル・アタッチ機能の詳細については、下記のサイトを参照してほしい。

 テスト・コードは、振る舞いが予測できるものでなければ記述できない。つまりデータベースのように、時間とともにデータの内容が変化していくものに対してテスト・コードを書くには、何らかの工夫を加える必要が生じる。その手段の1つがテスト用データベースの作成と配布なのだ。

 ファイル・アタッチ機能を利用したテスト用データベースを構築するには、まずVisual Studio 2005[ソリューション エクスプローラ]でSQL Serverデータベース・ファイルをプロジェクトに追加する。ちなみに、SQL Server 2005 Express Editionは、Visual Studio 2005をインストールするとデフォルトで一緒にインストールされる。

データベース・ファイル(.MDFファイル)の追加手順
[ソリューション エクスプローラ]でテスト用のデータベース・ファイル(.MDFファイル)をプロジェクトに追加する。
  [ソリューション エクスプローラ]に対して、使用するテスト用のデータベース・ファイル(本稿の例では、「MemberDB.mdf」)をドラッグ&ドロップする。もしくは、[ソリューション エクスプローラ]でプロジェクト・フォルダを右クリックしてコンテキスト・メニューを表示し、そこから[追加]−[既存項目の追加]を選択する。これにより、[既存項目の追加]ダイアログが表示されるので、フィルタの種類を「データ ファイル (*.xsd;*.xml;*.mdf;*.mdb)」に切り替えて、データベース・ファイルを選択する。これにより、[ソリューション エクスプローラ]にデータベース・ファイルの項目が追加される。
  ビルド時にデータベース・ファイルを出力ディレクトリにコピーするように設定する。具体的には、[プロパティ]ウィンドウで[出力ディレクトリにコピー]プロパティを「常にコピーする」(デフォルト)に設定する。これにより、ビルドするたびにデータベース・ファイルが初期状態に戻る。

 この画面で特に重要なのは、[出力ディレクトリにコピー]プロパティを「常にコピーする」(デフォルト)に設定することだ。これにより、ビルドするたびにデータベース・ファイルが初期状態に戻る。

 データベース・ファイルを追加する際には、[データ ソース構成ウィザード]ダイアログが表示されるので、そこで利用するテーブルなどのデータベース・オブジェクトを選択してデータセットを作成する。本稿の場合、これにより(上の画面でも分かるように)「MemberDataSet.xsd」が生成された。このデータセット(MemberDataSet.xsd)をダブルクリックすると、次の画面のように.xsdファイル(データセット)のデザイナが立ち上がる。また、追加したデータセットに対してテーブル・アダプタが自動作成されていることが確認できる。

データセットのテーブル・アダプタ
[ソリューション エクスプローラ]にデータベース・ファイルを追加して、[データ ソース構成ウィザード]ダイアログでデータセットを作成することで、自動的にテーブル・アダプタが利用できるようになる。
  追加したデータセット(この例では「MemberDataSet.xsd」)の項目が表示される。これをダブルクリックしてデザイナを開く。
  そのデータセットに対して自動的に生成されたテーブル・アダプタ(この例では「MemberTableAdapter」)。

 今回は、このテーブル・アダプタを利用して、テスト用データベースへアクセスする仕組みを実現する。

 通常はこの段階で、テーブル・アダプタのConnectionプロパティ(のConnectionStringプロパティ)に、データベース・ファイルへの接続文字列(例えば次のような値)が自動的に設定されている。

Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\MemberDB.mdf;Integrated Security=True;User Instance=True
接続文字列の値の例(本稿の場合)

 しかしファイル・アタッチ機能を利用するためには、この接続文字列で「Data Source=.\SQLEXPRESS」や「AttachDbFilename」を指定する必要がある(そのほかの制約については、前述のサイトを参照してほしい)。

 上記のテーブル・アダプタを利用したテスト・コードを以下に示す。なおこのサンプル・コードをコンパイルするには、事前に「nunit.framework」アセンブリと「System.Transactions」アセンブリへの参照が必要だ。

using System;
using System.Collections.Generic;
using System.Text;

using DataAccessLayer;
using DataAccessLayer.MemberDataSetTableAdapters;
using NUnit.Framework;
using System.Transactions;

namespace DataAccessLayer.Test
{
  [TestFixture]
  public class DataAccessLayerTester
  {
    private MemberTableAdapter memberAdapter;

    [SetUp]
    public void テスト・メソッド初期化()
    {
      memberAdapter = new MemberTableAdapter();
    }

    [TearDown]
    public void テスト・メソッド終了処理()
    {
    }

    [Test]
    public void メンバ情報の初期データは3件である()
    {
      MemberDataSet dataSet = new MemberDataSet();

      memberAdapter.Fill(dataSet.Member);
      Assert.AreEqual(3, dataSet.Member.Count);
    }
  }
}
テーブル・アダプタを利用したテスト・コード(DataAccessLayer.Test.cs)
このテスト・コードは、テーブル・アダプタを通じてテスト用データベースへアクセスする。

 このテスト・コードのNUnit-Guiでの実行結果は次の画面のようになる。

テーブル・アダプタを利用したテスト・コードの実行結果(NUnit-Guiの実行画面)
テーブル・アダプタを通じてテスト用のデータベース・ファイルへアクセスし、データアクセス層のテストを実行することが可能だ。なおこのテスト・コードを実行するには、.NET Framework 2.0対応のNUnitを使う必要がある。

.NET+アジャイルなら本当に幸せになれるのか?

 ここでは、SQL Server 2005 Express Editionのファイル・アタッチ機能を利用してテスト用データベースを実現する方法を紹介した。これに加えて、ビルド・ツールであるNAntなどを利用してデータベース管理者が管理しているテスト用データベースのデータベース・ファイルを各開発者の開発クライアントへ配布する仕組みも実現するとよいだろう。

●自動トランザクション機能の利用

.NET Framework 2.0から自動トランザクション機能(Transaction Scope)が新しく導入されている。通常、トランザクション制御では、明示的にトランザクションを開始し、最後にコミット/ロールバックを行うが、自動トランザクション機能を利用することでトランザクションのスコープ開始と処理成功(Complete)を宣言するだけで自動的にトランザクションを制御できる。自動トランザクション機能の詳細については、下記MSDNのページを参照してほしい。

 前述したが、良いテスト・コードの要件の1つとして、「繰り返し実行できる」ことが挙げられる。そのためには、テストの前後でアプリケーションの状態が変化しないようにテスト・コードを記述する必要がある。この自動トランザクション機能を利用することで、データベースに対する更新処理を自動的にロールバックできるため、データアクセス層でのテストを何度でも実行可能にできる。

// using指定と名前空間は
// 先ほどのテーブル・アダプタのテスト・コードと同じ

[TestFixture]
public class DataAccessLayerTester
{
  private MemberTableAdapter memberAdapter;
  private TransactionScope transactionScope;

  [SetUp]
  public void テスト・メソッド初期化()
  {
    transactionScope = new TransactionScope();
    memberAdapter = new MemberTableAdapter();
  }

  [TearDown]
  public void テスト・メソッド終了処理()
  {
    transactionScope.Dispose();
  }

  [Test]
  public void メンバ情報の初期データは3件である()
  {
    MemberDataSet dataSet = new MemberDataSet();

    memberAdapter.Fill(dataSet.Member);
    Assert.AreEqual(3, dataSet.Member.Count);
  }

  [Test]
  public void メンバを登録できるかな()
  {
    string memberID = Guid.NewGuid().ToString();
    string loginID = "user";
    string password = "password";
    string firstName = "太郎";
    string lastName = "アジャイル";
    string mailAddress = "test@nagile.com";

    int result =memberAdapter.Insert(
      memberID,
      loginID,
      password,
      firstName,
      lastName,
      mailAddress,
      DateTime.Now);

    Assert.AreEqual(1, result, "メンバの登録に失敗しました");
  }

  [Test]
  public void メンバを削除できるかな()
  {
    string memberID = "0000000001";

    int result = memberAdapter.Delete(memberID);
    Assert.AreEqual(1, result, "メンバの削除に失敗しました");
  }

  [Test]
  public void メンバ情報を更新できるかな()
  {
    string memberID = "0000000002";
    string loginID = "user2";
    string password = "password2";
    string firstName = "ブラック";
    string lastName = "アジャイル";
    string mailAddress = "black@nagile.com";

    int result = memberAdapter.Update(
      loginID,
      password,
      firstName,
      lastName,
      mailAddress,
      memberID,
      memberID);

    Assert.AreEqual(1, result, "メンバ情報の更新に失敗しました");
  }
}
自動トランザクション機能を利用したテスト・コード(DataAccessLayer.Test.cs)
それぞれのテスト・メソッド内で、テーブル・アダプタを利用して、SELECT/INSERT/UPDATE/DELETEなどのSQL命令処理を呼び出している。TransactionScope.Completeメソッドが呼び出されないまま、TearDownメソッド内でTransactionScope.Disposeメソッドが呼び出されるため、テスト・メソッド内の処理はすべてロールバック(破棄)される。

 このテスト・コードのNUnit-Guiでの実行結果は以下のようになる。

自動トランザクション機能を利用したテスト・コードの実行結果(NUnit-Guiの実行画面)
自動トランザクションを利用しているので、テスト前とテスト後のデータベースの状態が変わらずに何度でも同じテストを実行できる。

 ちなみに、NUnitの将来のバージョンではテストで更新されたデータを自動的にロールバックするRollback属性の機能が計画されている。Rollback属性の機能が実装されると、データベースの更新処理に関するテスト・コードの記述がさらに容易になるだろう。

   

えっと……ファイル・アタッチ機能……テーブル・アダプタ……自動トランザクション機能……たくさんありすぎて、全部覚えられません……(涙)。

覚えることが重要なのではない。データアクセス層でのテストファーストは、決まったツールややり方があるわけではない。だから自分なりのアイデア/工夫を加えてテスト・コードを記述することが大切だ。今回紹介したアイデア以外にも、自分なりのデータアクセス層のテストを考えて、試してみるとよいだろう。まさに継続的な改善だな。

はあ……頑張ってみます……。

できないことであれこれ悩むのではなく、いま自分にできることは何かを考え、とにかく始めてみることだな。それがNAgileへの道だ。今日は、最後にこの言葉をお前に贈ろう。

NAgileへの道
by 上杉鷹山
(毛筆版)

 

 毛筆で書いたから、壁に貼って……。

う゛……またそうきますか(汗)。

5. まとめ

 さて、今回はNAgileの設計に対する考え方や実践方法について考えてみた。常に、現時点の問題を解くシンプルな設計を心掛け、設計を改善するということの有効性と重要性を実感していただきたい。そしてこれを実現する近道であるテストファーストにぜひトライしてほしい。

 次回はビルドの自動化(NAnt)について解説する予定だ。お楽しみに。End of Article

(Illustrated by 正木茶丸

 

 INDEX
  NAgileで始める実践アジャイル開発
  第3回 ソフトウェアの良い設計を行うコツ
    1.NAgileにおける設計のコツ「インクリメンタル設計」
    2.テストファーストの基礎ツール
  3.データアクセス層でのテストファースト
 
インデックス・ページヘ  「NAgileで始める実践アジャイル開発」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間