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:
IMyDataContextnamespace 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.
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
Czy masz konkretny powód, aby nie używać szyderczych ram? –
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