私は次のコードを持っています:
public class Foo
{
public string Name { get; set; }
}
//query object pattern
public class FooQuery
{
private string _startsWith;
public FooQuery NameStartsWith(string startsWith)
{
_startsWith = startsWith;
return this;
}
public List<Foo> Execute(IQueryable<Foo> someContext)
{
if (!string.IsNullOrWhiteSpace(_startsWith))
someContext = someContext.Where(f => f.Name.StartsWith(_startsWith));
return someContext.ToList();
}
}
public interface IFooService
{
List<Foo> FindByNameStartsWith(string startsWith);
}
public class FooService : IFooService
{
private readonly IFooRepository _fooRepository;
public FooService(IFooRepository fooRepository)
{
_fooRepository = fooRepository;
}
public List<Foo> FindByNameStartsWith(string startsWith)
{
var query = new FooQuery().NameStartsWith(startsWith);
return _fooRepository.Find(query);
}
}
public interface IFooRepository
{
List<Foo> Find(FooQuery query);
}
public class FooRepository : IFooRepository
{
public List<Foo> Find(FooQuery query)
{
var someContext = new List<Foo>().AsQueryable(); //would be EF/Mongo, etc
return query.Execute(someContext);
}
}
基本的に、クエリオブジェクト「FooQuery」を通知し、渡されたメソッドパラメータに基づいてその状態を設定するサービス「FooService」があります。次に、サービスはクエリをリポジトリ「FooRepository」に渡し、そこでデータアクセスを行います。FooQueryは、プロパティを介してその状態を意図的に公開しません。代わりに、より高度な制御のためのメソッドを公開します。FooServiceがクエリオブジェクトを正しく作成したことを単体テストする必要があります。
FooQueryの状態は単体テストでは表示されないため、これは課題です。いくつかのオプションがありますが、すべてにおいがするようです。
FooQueryの状態を読み取り専用プロパティとして公開し、単体テストで、これらのプロパティがリポジトリに渡されたときに有効であることを確認します。(クエリの状態をチェックすることは、コールバックを使用してフレームワークをモックすることで技術的に可能です。)テスト目的で状態を開いてコードを変更する必要があるため、これは好きではありません。
コードをそのままにして、サービスメソッドから生成された結果がクエリオブジェクトからの結果と同じであることをテストします。単体テストが大きくなり、決定性が低くなり、冗長になるため、これは好きではありません(結果を確認するときに、クエリオブジェクト自体に対して非常によく似たテストを行う必要があります)
FooQueryをインターフェースでラップし、fooサービスに注入されるファクトリを作成します。次に、モックされたクエリで呼び出された適切なメソッドをテストできます。しかし、それでも私は工場自体の挑戦的なテストを余儀なくされています。
テストを容易にするためにこのコードをテスト/リファクタリングするための提案をいただければ幸いです。