Comment puis-je simuler un DataServiceQuery à des fins de test unitaire?Mocking a DataServiceQuery <TElement>
Détails long suivent: Imaginer une application ASP.NET MVC, où les pourparlers de contrôleur à un DataService ADO.NET qui encapsule le stockage de nos modèles (par exemple nous allons bien être en train de lire une liste des clients). Avec une référence au service, nous obtenons une classe générée héritant de 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 */
}
}
Le contrôleur pourrait être:
namespace Sample.Controllers
{
public class CustomerController : Controller
{
private IMyDataContext context;
public CustomerController(IMyDataContext context)
{
this.context=context;
}
public ActionResult Index() { return View(context.Customers); }
}
}
Comme vous pouvez le voir, j'ai utilisé un constructeur qui accepte une instance IMyDataContext si que nous pouvons utiliser une maquette dans notre test unitaire:
[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 et MyDataContext doivent implémenter la même interface IMyDataContext:
namespace Sample.Services
{
public interface IMyDataContext
{
DataServiceQuery<Customer> Customers { get; }
/* and more */
}
}
Cependant, lorsque nous essayons de mettre en œuvre la classe MockContext, nous rencontrons des problèmes en raison de la nature de DataServiceQuery (qui, pour être clair, nous utilisons dans l'interface IMyDataContext simplement parce que ce type de données que nous avons trouvé dans la classe MyDataContext générée automatiquement avec laquelle nous avons démarré). Si nous essayons d'écrire:
public class MockContext : IMyDataContext
{
public IList<Customer> CustomersToReturn { set; private get; }
public DataServiceQuery<Customer> Customers { get { /* ??? */ } }
}
Dans les clients getter nous aimerions instancier une instance DataServiceQuery, remplir avec les clients dans CustomersToReturn, et le retourner. Les problèmes que je rencontre:
1 ~ DataServiceQuery n'a aucun constructeur public; pour instancier celui que vous devez appeler CreateQuery sur un DataServiceContext; voir MSDN
2 ~ Si je fais le MockContext héritera de DataServiceContext ainsi, et appeler CreateQuery pour obtenir un DataServiceQuery à utiliser, le service et la requête doivent être liés à un URI valide et, lorsque je tente de itérer ou accéder aux objets dans la requête, il va essayer de s'exécuter par rapport à cet URI. Autrement dit, si je change le MockContext en tant que tel:
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;
}
}
}
}
Ensuite, dans le test unitaire, nous obtenons une erreur sur la ligne marquée comme la ligne A, parce que http://www.contoso.com ne héberge notre service. La même erreur est déclenchée même si LINE A essaie d'obtenir le nombre d'éléments dans les modèles. Merci d'avance.
Dror, merci pour l'idée, mais pour le moment nous ne sommes pas utiliser un cadre moqueur. Nous serions intéressés de voir s'il y a une solution qui ne repose pas sur un. Encore, merci – FOR
Avez-vous une raison spécifique de ne pas utiliser un cadre moqueur? –
En général, pas de raison particulière. Nous pourrions l'introduire, mais il est peu probable que nous le fassions du jour au lendemain pour cette tâche spécifique. Donc, disons que pour l'instant nous aimerions trouver une solution sans ajouter un cadre moqueur. – FOR