2009-09-17 7 views
5

Jak mogę sfałszować DataServiceQuery dla celów testowania jednostkowego?Wyśmiewanie DataServiceQuery <TElement>

Długie Szczegóły śledzić: Wyobraź aplikacji ASP.NET MVC, gdzie rozmowy kontrolerów do DataService ADO.NET że kapsułkuje przechowywanie naszych modeli (na przykład ze względu będziemy czytać listę klientów). W odniesieniu do usług, możemy uzyskać wygenerowany klasa dziedziczy z 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 */ 
    } 
} 

Kontroler może być:

namespace Sample.Controllers 
{ 
    public class CustomerController : Controller 
    { 
    private IMyDataContext context; 

    public CustomerController(IMyDataContext context) 
    { 
     this.context=context; 
    } 

    public ActionResult Index() { return View(context.Customers); } 
    } 
} 

Jak widać, użyłem konstruktora, która akceptuje IMyDataContext wystąpienie tak że możemy użyć makiety w naszym teście jednostka:

[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 i MyDataContext trzeba zaimplementować ten sam interfejs:

IMyDataContext
namespace Sample.Services 
{ 
    public interface IMyDataContext 
    { 
    DataServiceQuery<Customer> Customers { get; } 
    /* and more */ 
    } 
} 

Jednak, gdy staramy się realizować klasę MockContext, że napotkasz problemy ze względu na charakter DataServiceQuery (które, aby być jasne, używamy w interfejsie IMyDataContext po prostu dlatego, że jest to typ danych znaleźliśmy w automatycznie wygenerowanej klasie MyDataContext, od której zaczęliśmy). Jeśli spróbujemy napisać:

public class MockContext : IMyDataContext 
{ 
    public IList<Customer> CustomersToReturn { set; private get; } 

    public DataServiceQuery<Customer> Customers { get { /* ??? */ } } 
} 

W Klientów getter chcielibyśmy instancji instancji DataServiceQuery, wypełnić go z Klientami w CustomersToReturn i odesłać go. Problemy, które napotkam:

1 ~ DataServiceQuery nie ma publicznego konstruktora; aby utworzyć instancję, należy wywołać CreateQuery w DataServiceContext; zobacz MSDN

2 ~ Jeśli robię MockContext dziedziczą DataServiceContext jak również i zadzwonić CreateQuery dostać DataServiceQuery do użytkowania, obsługi i zapytania muszą być przywiązane do poprawnego URI, a gdy próbuję iteracyjne lub uzyskać dostęp do obiektów w zapytaniu, spróbuje wykonać to na podstawie tego identyfikatora URI. Innymi słowy, jeśli zmienię MockContext jako takie:

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; 
     } 
    } 
    } 
} 

Następnie w badanej jednostki, otrzymujemy błąd na linii oznaczony jako linia A, ponieważ http://www.contoso.com nie udostępniać nasze usługi. Ten sam błąd jest wyzwalany, nawet jeśli LINE A próbuje uzyskać liczbę elementów w modelach. Z góry dzięki.

Odpowiedz

0

[Zastrzeżone - Pracuję w Typemock]

Czy za pomocą ramy szyderczy?

Można użyć Typemock Izolator stworzyć fałszywy wystąpienie DataServiceQuery:

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

I można utworzyć podobny fałszywy DataServiceContext i ustaw jego zachowanie zamiast próbować go odziedziczyć.

+0

Dror, dzięki za pomysł, ale na razie nie używamy żadnych ram szyderczy. Chcielibyśmy zobaczyć, czy istnieje rozwiązanie, które nie opiera się na jednym. Mimo wszystko, dziękuję – FOR

+0

Czy masz konkretny powód, aby nie używać szyderczych ram? –

+0

Ogólnie, bez konkretnego powodu. Możemy go wprowadzić, ale jest mało prawdopodobne, że zrobimy to z dnia na dzień dla tego konkretnego zadania. Powiedzmy więc, że na razie chcielibyśmy znaleźć rozwiązanie bez dodawania szyderczych ram. – FOR

4

że rozwiązanie to tworząc interfejs IDataServiceQuery dwie implementacje:

  • DataServiceQueryWrapper
  • MockDataServiceQuery

IDataServiceQuery Następnie używać wszędzie tam, gdzie już wcześniej używany 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 zajmuje DataServiceQuery w jego konstruktora, a następnie delegatów wszystkich funkcjonalności do kwerendy przekazany w. Podobnie MockDataServiceQuery bierze IQueryable i delegatów wszystko, co możliwe, aby w zapytaniu.

Dla próbnych metod IDataServiceQuery, obecnie po prostu zwracam this, ale można zrobić coś, aby wyśmiać funkcjonalność, jeśli chcesz.

Na przykład:

// (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; 
} 
Powiązane problemy