ORM に NHibernate を使用する MVC アプリがあります。各コントローラーは ISession 構築パラメーターを受け取り、ドメイン モデル オブジェクトに対して CRUD 操作を実行するために使用されます。例えば、
public class HomeController : Controller
{
public HomeController(ISession session)
{
_session = session;
}
public ViewResult Index(DateTime minDate, DateTime maxDate)
{
var surveys = _session.CreateCriteria<Survey>()
.Add( Expression.Like("Name", "Sm%") )
.Add( Expression.Between("EntryDate", minDate, maxDate) )
.AddOrder( Order.Desc("EntryDate") )
.SetMaxResults(10)
.List<Survey>();
// other logic that I want to unit test that does operations on the surveys variable
return View(someObject);
}
private ISession _session;
}
MoqまたはRhinoMocksを使用してISessionオブジェクトをモックすることにより、実際にデータベースにアクセスすることなく、このコントローラーを単独で単体テストしたいと思います。ただし、単体テストで ISession インターフェイスをモック化するのは非常に困難です。これは、多数の呼び出しを連鎖させる流暢なインターフェイスを介して使用されるためです。
1 つの代替手段は、リポジトリ パターンを介して ISession の使用をラップすることです。次のようなラッパークラスを書くことができます:
public interface IRepository
{
List<Survey> SearchSurveyByDate(DateTime minDate, DateTime maxDate);
}
public class SurveyRepository : IRepository
{
public SurveyRepository(ISession session)
{
_session = session;
}
public List<Survey> SearchSurveyByDate(DateTime minDate, DateTime maxDate)
{
return _session.CreateCriteria<Survey>()
.Add( Expression.Like("Name", "Sm%") )
.Add( Expression.Between("EntryDate", minDate, maxDate) )
.AddOrder( Order.Desc("EntryDate") )
.SetMaxResults(10)
.List<Survey>();
}
private ISession _session;
}
次に、ISession 引数の代わりに IRepository コンストラクター引数を取るようにコントローラーを書き直すことができます。
public class HomeController : Controller
{
public HomeController(IRepository repository)
{
_repository = repository;
}
public ViewResult Index(DateTime minDate, DateTime maxDate)
{
var surveys = _repository.SearchSurveyByDate(minDate, maxDate);
// other logic that I want to unit test that does operations on the surveys variable
return View(someObject);
}
private IRepository _repository;
}
この 2 番目のアプローチは、単一のメソッド呼び出しであるため、IRepository インターフェイスは ISession インターフェイスよりもモックするのがはるかに簡単であるため、単体テストがはるかに簡単になります。ただし、次の理由から、このルートをたどりたくありません。
1) 単体テストを簡単にするためだけに、まったく新しい抽象化レイヤーを作成し、さらに複雑にすることは、本当に悪い考えのように思えます。
2) ISession インターフェイスはすでにリポジトリのようなインターフェイスであるため、nHibernate でリポジトリ パターンを使用するという考えに反対するコメントがたくさんあります。(特に Ayende の投稿をこことここで参照してください)、私はこの解説に同意する傾向があります。
私の質問は、ISession オブジェクトをモックすることで初期実装を単体テストできる方法はありますか? そうでない場合、リポジトリ パターンを使用して ISession クエリをラップする唯一の手段ですか、それともこれを解決できる他の方法がありますか?