LoginSignup
3
2

More than 5 years have passed since last update.

xUnitでのユニットテスト実行時に、NHibernate + SQLiteなインメモリDBをテストに利用する

Posted at

概要

xUnitでユニットテストをする際、テスト実行のためにデータベースが必要となるテストケースをどのように書いたら良いかまとめました。

ここではNHibernate + SQLiteの組み合わせの場合のコードを例示していますが、Entity Framework + SQL Databaseなどの場合も同じような考え方で実装できます。

実装方針

テストケースを記述したクラスでIClassFixture<TFixture>を継承しておくと、xUnitによってテストが実行される前にTFixtureクラスがインスタンス化され、そのインスタンスがテストケースのクラスのコンストラクタに渡されます。

従って、今回のようにデータベースを必要とするテストでは、次のような方針でテストコードを実装します。

  1. TFixtureクラスにDB周りの初期化コードを書く
  2. テストケースのクラスでは、IClassFixture<TFixture>を継承して、コンストラクタでTFixtureインスタンスを受け取る
  3. テストコード内でTFixtureインスタンスからDBのセッションを取り出してDB操作する

実際のコード例

SQLiteのセッションを生成するためのFixtureクラス例

Fixtureクラス側では、インメモリなSQLiteに接続するようなコードを、NHibernateとFluentNHibernateを使って実装しました。

なお、IDisposableを実装しておくと、xUnitがテスト終了時にDispose()を呼び出してくれるため、DBの接続を閉じるコードなどを書いておくことができます。

またSchemaExportを利用し、O/Rマッパーで定義されているマッピング情報を元に、SQLite上に必要なテーブルを自動的に作った上でDBセッションをテストケースに渡す、ということも実装しています。

using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.PlatformAbstractions;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using System;
using System.Linq;
using System.Reflection;
using Xunit;
using Xunit.Abstractions;

public class SQLiteInMemoryDatabaseFixture: IDisposable
{
    private static ISessionFactory sessionFactory;
    private static Configuration configuration;
    public static ISessionFactory SessionFactory {
        get
        {
            if (sessionFactory == null)
            {
                sessionFactory = Fluently.Configure()
                    .Database(
                        SQLiteConfiguration.Standard
                            .InMemory()
                            .ShowSql()
                        )
                    .Mappings(m =>
                    {
                        m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly());
                    })
                    .ExposeConfiguration(cfg => {
                        configuration = cfg;
                    })
                    .BuildSessionFactory();
            }
            return sessionFactory;
        }
    }

    public ISession GetSession()
    {
        ISession session = SessionFactory.OpenSession();

        var export = new SchemaExport(configuration);
        export.Execute(true, true, false, session.Connection, null);

        return session;
    }


    #region IDisposable Support
    private bool disposedValue = false; // 重複するDispose()呼び出しを検出する

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                if (sessionFactory != null)
                    sessionFactory.Dispose();
            }

            sessionFactory = null;
            configuration = null;

            disposedValue = true;
        }
    }

    //IDisposableを実装しておくと、xUnitがテスト終了時に呼び出してくれる
    // https://xunit.github.io/docs/shared-context.html#class-fixture
    public void Dispose()
    {
        Dispose(true);
    }
    #endregion

}

テストケースでFixtureクラスを利用する

テストケースがIClassFixture<TFixture>を継承していると、テストケースのコンストラクタにTFixtureのインスタンスが渡されます。
渡されたfixtureをフィールドに格納しておき、テストケース内から適宜利用する形になります。


public class MyServiceTest: IClassFixture<SQLiteInMemoryDatabaseFixture>
{
    private readonly SQLiteInMemoryDatabaseFixture _fixture;

    public MyServiceTest(SQLiteInMemoryDatabaseFixture fixture)
    {
        _fixture = fixture;
    }

    [Fact]
    public void InterfaceImplimentationTest()
    {
        var session = _fixture.GetSession();
        var uowFactory = new NHibernateUnitOfWorkFactory(session: session);

        // テストケースが続く.......
    }
}

利用したNuGetライブラリ

project.jsonで読み込んでいるライブラリはこんな感じです。

"dependencies": {
    "NHibernate": "4.0.4.4000",
    "xunit": "2.1.0",
    "dotnet-test-xunit": "1.0.0-rc2-build10025",
    "xunit.runner.visualstudio": "2.1.0",
    "FluentNHibernate": "2.0.3",
    "FluentMigrator": "1.6.2",
    "FluentMigrator.Tools": "1.6.2",
    "System.Data.SQLite": "1.0.101",
    "System.Data.SQLite.Core": "1.0.101",
    "System.Data.SQLite.Linq": "1.0.101",
    "System.Data.SQLite.x64": "1.0.101"
}
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2