単体テストの目的で DataServiceQuery をモックするにはどうすればよいですか?
長い詳細は次のとおりです。コントローラーがモデルのストレージをカプセル化する ADO.NET DataService と通信する ASP.NET MVC アプリケーションを想像してください (たとえば、顧客のリストを読み取るため)。サービスへの参照を使用して、DataServiceContext から継承する生成されたクラスを取得します。
namespace Sample.Services
{
public partial class MyDataContext : global::System.Data.Services.Client.DataServiceContext
{
public MyDataContext(global::System.Uri serviceRoot) : base(serviceRoot) { /* ... */ }
public global::System.Data.Services.Client.DataServiceQuery<Customer> Customers
{
get
{
if((this._Customers==null))
{
this._Customers = base.CreateQuery<Customer>("Customers");
}
return this._Customers;
}
}
/* and many more members */
}
}
コントローラーは次のようになります。
namespace Sample.Controllers
{
public class CustomerController : Controller
{
private IMyDataContext context;
public CustomerController(IMyDataContext context)
{
this.context=context;
}
public ActionResult Index() { return View(context.Customers); }
}
}
ご覧のとおり、単体テストでモックを使用できるように、IMyDataContext インスタンスを受け入れるコンストラクターを使用しました。
[TestFixture]
public class TestCustomerController
{
[Test]
public void Test_Index()
{
MockContext mockContext = new MockContext();
CustomerController controller = new CustomerController(mockContext);
var customersToReturn = new List<Customer>
{
new Customer{ Id=1, Name="Fred" },
new Customer{ Id=2, Name="Wilma" }
};
mockContext.CustomersToReturn = customersToReturn;
var result = controller.Index() as ViewResult;
var models = result.ViewData.Model;
//Now we have to compare the Customers in models with those in customersToReturn,
//Maybe by loopping over them?
foreach(Customer c in models) //*** LINE A ***
{
//TODO: compare with the Customer in the same position from customersToreturn
}
}
}
MockContext と MyDataContext は、同じインターフェイス IMyDataContext を実装する必要があります。
namespace Sample.Services
{
public interface IMyDataContext
{
DataServiceQuery<Customer> Customers { get; }
/* and more */
}
}
ただし、MockContext クラスを実装しようとすると、DataServiceQuery の性質が原因で問題が発生します (明確にするために、IMyDataContext インターフェイスで使用しているのは、自動生成された MyDataContext で見つかったデータ型であるためです)。私たちが始めたクラス)。書き込もうとすると:
public class MockContext : IMyDataContext
{
public IList<Customer> CustomersToReturn { set; private get; }
public DataServiceQuery<Customer> Customers { get { /* ??? */ } }
}
Customers ゲッターで、DataServiceQuery インスタンスをインスタンス化し、CustomersToReturn に Customers を入力して、それを返します。私が遭遇する問題:
1~ DataServiceQuery にはパブリック コンストラクターがありません。インスタンス化するには、DataServiceContext で CreateQuery を呼び出す必要があります。MSDNを参照
2~ MockContext も DataServiceContext から継承し、CreateQuery を呼び出して使用する DataServiceQuery を取得する場合、サービスとクエリを有効な URI に関連付ける必要があり、クエリ内のオブジェクトを反復またはアクセスしようとすると、その URI に対して実行を試みます。つまり、MockContext を次のように変更すると:
namespace Sample.Tests.Controllers.Mocks
{
public class MockContext : DataServiceContext, IMyDataContext
{
public MockContext() :base(new Uri("http://www.contoso.com")) { }
public IList<Customer> CustomersToReturn { set; private get; }
public DataServiceQuery<Customer> Customers
{
get
{
var query = CreateQuery<Customer>("Customers");
query.Concat(CustomersToReturn.AsEnumerable<Customer>());
return query;
}
}
}
}
次に、単体テストでは、LINE A としてマークされた行でエラーが発生します。これは、 http://www.contoso.comがサービスをホストしていないためです。LINE Aがモデルの要素数を取得しようとしても、同じエラーがトリガーされます。前もって感謝します。