5

単体テストの目的で 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がモデルの要素数を取得しようとしても、同じエラーがトリガーされます。前もって感謝します。

4

2 に答える 2

4

IDataServiceQuery2 つの実装を持つインターフェイスを作成することで、これを解決しました。

  • DataServiceQueryWrapper
  • MockDataServiceQuery

次に、IDataServiceQuery以前に を使用していた場所で使用しDataServiceQueryます。

public interface IDataServiceQuery<TElement> : IQueryable<TElement>, IEnumerable<TElement>, IQueryable, IEnumerable
{
    IDataServiceQuery<TElement> Expand(string path);

    IDataServiceQuery<TElement> IncludeTotalCount();

    IDataServiceQuery<TElement> AddQueryOption(string name, object value);
}

DataServiceQueryWrapperは、そのDataServiceQueryコンストラクターで を受け取り、渡されたクエリにすべての機能を委譲します。同様に、 はMockDataServiceQueryを受け取り、IQueryableできることすべてをクエリに委譲します。

モックIDataServiceQueryメソッドについては、現在は を返すだけthisですが、必要に応じて機能をモックするために何かを行うこともできます。

例えば:

// (in DataServiceQueryWrapper.cs)
public IDataServiceQuery<TElement> Expand(string path)
{
    return new DataServiceQueryWrapper<TElement>(_query.Expand(path));
}

 

// (in MockDataServiceQuery.cs)
public IDataServiceQuery<TElement> Expand(string path)
{
    return this;
}
于 2010-04-14T19:16:43.537 に答える
0

[免責事項 - Typemock で働いています]

モッキング フレームワークの使用を検討したことはありますか?

Typemock Isolator を使用して、DataServiceQuery の偽のインスタンスを作成できます。

var fake = Isolate.Fake.Instance<DataServiceQuery>();

同様の偽の DataServiceContext を作成し、継承を試みる代わりにその動作を設定できます。

于 2009-09-17T14:30:57.967 に答える