redwarrior’s diary

C#, ASP.NET, WEB, G*などの雑多な情報

テストプロジェクトからメインプロジェクトのDBを参照する

この記事は Windows & Microsoft技術 基礎 Advent Calendar 2015 - Qiita の15日目です。

アドベントカレンダーからいらした方、初めまして。今年の始めのあたりからブログを書き始めた redwarriror と申します。

普段はC#等のMicrosoft系の技術を中心に書いています。今回の内容も他のアドベントカレンダーに書いた方が良い内容なのかもしれませんが、参加するかどうか&テーマに悩んでいる間に埋まってしまいました(苦笑)。いざテーマを決めた後、このアドベントカレンダーが丁度空いていたので、思い切って参加してみました。

さて、さっそくですが、ASP.NET MVC/Web APIで、DBに接続してデータを取得するプログラムのテストってどうやっていますか?

自分はテンプレートからプロジェクトを新規作成するときに、テストプロジェクトも一緒に作成するようにしてます。

そうするとテストプロジェクトが別に用意されて設定ファイルも別になります。この状態でテストクラスからどうやって上記プログラムのテストをするかという話です。

検討

いろんな方法が考えられます。

  • モックオブジェクトを使用する
  • テストプロジェクトにもDBを用意する
  • ローカルのDBを使用しない
  • テストプロジェクトを作成しない。

モックオブジェクトを使用する

良いアイデアだと思います。大部分はこれでまかなえると思います。ライブラリとしては、自分はMoqをよく使います。ただし、これだとDBに接続できるかというテストは出来ませんし、モックオブジェクトの設定が大量・複雑になりやすいです。

テストプロジェクトにもDBを用意する

開発中はLocalDBをよく使用するので準備はこれが簡単かもしれません。ただし、DBが2か所にあるということで、マスタ系の初期データの投入プログラムが2か所に重複しますし、マイグレーションの時に困ります。

ローカルのDBを使用しない

LocalDBを使用せずDBサーバーを建てるのも一つの手です。ただし、DBが共有の場合は他への影響が気になってデータの追加・削除がやりづらいですし、ローカルのDBサーバーをインストールする権限がないかもしれません。

テストプロジェクトを作成しない。

メインプロジェクトにテストクラスを作成する方法です。Microsoftの流儀に沿っていない気がするので、若干の不安と気持ち悪さがあります。あと、ビルドやデプロイ時にテストクラスやテスト用ライブラリを除外する方法を知らないと、テストコードも含んだ実行ファイルが作成されてしまいます。

結局

やりたい事はテストプロジェクトからメインプロジェクトのDBにアクセスしたいだけなんだから、もっと良い方法があるはずだと思って、ネットサーフィンをし続けた結果、DataDirectoryの値を変更するという方法にたどり着きました。(参考サイト

やり方

やり方としては、まずテストプロジェクトにEntity Frameworkをインストールします。次にメインプロジェクトのWeb.configのconnectionStringsの値を、テストプロジェクトのApp.configにコピーします。

<connectionStrings>
  <add name="TestContext" connectionString="Data Source=(localdb)\v11.0; Initial Catalog=TestContext-20150902120557; Integrated Security=True; MultipleActiveResultSets=True; AttachDbFilename=|DataDirectory|TestContext-20150902120557.mdf" providerName="System.Data.SqlClient" />
</connectionStrings>

そうしたら以下のクラスを DataDirectoryInitialize.cs という名前でテストプロジェクトに作成します。クラスの作成場所はどこでも良いみたいなので、DALフォルダを作成してその下に置きました。

public class DataDirectoryInitialize
{
    [AssemblyInitialize]
    public static void AssemblyInitialize(TestContext context)
    {
        var directory = AppDomain.CurrentDomain.BaseDirectory;
        var replace = directory.Replace(@".Tests\bin\Debug", string.Empty);
        var combine = Path.Combine(replace, "App_Data");
        AppDomain.CurrentDomain.SetData("DataDirectory", combine);
    }
}

上記ではテストプロジェクトの命名ルールを活かして、メインプロジェクトのApp_Dataフォルダ(メインプロジェクトのDataDirectoryの場所)を指定するように設定しています。

これでTestContextをnewした時に、メインプロジェクトのDBに接続することが出来ます。また、IDatabaseInitializerを継承したクラスによる初期データ投入も行われます。

あとはテストメソッドの中で、以下のようにTransactionScopeを使用すれば、テストごとに自由にDBを触ることが出来て、後始末もしてくれます。

[TestMethod]
public void ConnectionTest()
{
    using (TransactionScope transactionScope = new TransactionScope()) //終了時にロールバック
    {
        using (TestContext context = new TestContext()) //メインProjectのデータベースを取得
        {
            // Usersテーブルの初期化,他のテーブルは残す
            context.Users.RemoveRange(context.Users); 
            context.SaveChanges();
            
            // テストデータの追加、テストの実施などを記述する
            ...
        }
    }
}

以上です。

ちなみに参考にしたのは昨年書かれたブログ(参考サイト)ですが、そこから参照されているのはさらに7年前のブログでした。ただ自分が知ったのは最近で、結構ためになったので書くことにしました。

開発環境:

Windows 8.1

VisualStudio 2015 Professional

ASP.NET MVC(or Web API) 5.2.3

Entity Framework 6.1.3

MSTest

参考サイト

接続文字列に"|DataDirectory|"を使用したMSTestプロジェクトでやっとくこと - なか日記