ASP.Net MVC2: Met eenvoudig testbare database laag

Sinds enkele weken ben ik bezig met het bouwen van een database applicatie
met behulp van ASP.Net MVC2. Hoewel ik een hoge kennisdrempel verwachtte, bleek dit enorm mee te vallen. De leercurve is eigenlijk best vlak.

Als ik terugkijk op die weken dan moet ik constateren dat het loslaten van
ingesleten ASP.Net gewoontes het moeilijkste was. MVC2 in zijn huidige vorm kent niet of nauwelijks visuele controls dus de VS2010 toolbox kan gesloten blijven. Ook UserControls zijn in eerste instantie niet aan de orde. Wat overblijft, is het plaatsen van ASPX pagina’s in de Solution. Deze pagina’s zijn gevuld met
HTML en MVC2 specifieke statements om doorgegeven data uit te lezen en te
tonen.

TestDrive

Nu wil ik niet beweren dat MVC2 de ‘silver bullet’ is, in tegendeel. Maar
MVC2 neemt een ruime plaats in tussen ‘klassiek’ ASP.Net en de Dynamic Websites . Ook leeft het gewoon naast de technieken rond SharePoint. Maar voor mijn gevoel geeft MVC de productiviteit terug aan de ontwikkelaars welke gewoon op een prettige manier CRUD applicaties in .Net willen bouwen.

Want MVC2 werkt prima samen met het Entitiy Framework (EF2). Deze ORM mapper heeft ook een evolutie doorgemaakt en sluit daardoor nu prettig aan bij MVC2. EF4 biedt oa. de mogelijk tot lazy loading wat weer tot het sneller bouwen van een Master Detail scherm leidt. Voorbeeldje: Als je een ProductOrder in een View kunt tonen, dan heb je binnen de View automatisch ook de beschikking over het gerelateerde Product, de Klant en de OrderRegels. Je hoeft dus niet direct een gespecialiseerde ViewModel samen te stellen.

Ook is het MVC als concept voorbereid op Unittesting. Hier wordt door de
collega’s al hoog over opgegeven dus ik sluit me hier gewoon bij aan. Maar wat
minder vaak geroepen wordt, is de mogelijkheid tot het mocken van de database
toegang. Dit is standaard niet via de T4 generatie in de controllers gegenereerd maar met slechts een paar handelingen is dit te realiseren. Hoe te beginnen? Uitgangspunt is dat in Controllers geen enkele LinqToSql statement geplaatst wordt. Ook mag de Controller niet direct de EF4 context bezitten als private member.

Deze verantwoording wordt namelijk overgelaten aan een apart te instanciëren
klasse, genaamd Repository. Deze kan via LinQ de EF4 context aanroepen en deze queries als publieke methodes aanbieden. Van de repository klass kan een
interface afgeleid worden, genaamd IRepository, welke de publieke methodes
beschikbaar stelt als contract.

public class ContactRepository : IContactRepository
{
  // Put your EF context here
  private Context _Context = new Context();

  public List<Contact> List(string filter)
  {
    // Do your LinQ magic here
    return null;
  }
}

public interface IContactRepository
{
  List<Contact> List(string filter);
}

De magie is om op de Controller een private field aan te maken van
het type IRepository. Via een extra Constructor met een IRepository parameter
kan dan een instantie van IRepository doorgegeven worden. Deze Constructor is
voor de unittests. Want via een Mocking framework (Mock-You)
kan dan een IRepository mock gegenereerd worden.

public class ContactController
{
  private IContactRepository _ContactRepository;

  public ContactController() : this(new ContactRepository())
  {
  }

  public ContactController(IContactRepository contactRepository)
  {
    _ContactRepository = contactRepository;
  }

  public ViewResult Index()
  {
    return View(_ContactRepository.List());
  }
}

[TestMethod]
public void TestMethod_Contact_List()
{
  var mock = new Mock<IContactRepository>();
  mock.Setup(framework => framework.List("Sander"))
    .Returns(new List<Contact>() {new Contact(){Naam="Alex"}})
    .AtMostOnce();
  IContactRepository contactRepository = mock.Object;
  List<Contact> contacts = contactRepository.List("Alex");
  Assert.IsNotNull(contacts);
  Assert.AreEqual(1, contacts.Count);
  mock.Verify(framework => framework.List("Alex"));
}

Maar runtime moet de repository klasse aangeroepen worden. Plaats
dus op de Controller ook een standaard Constructor (zonder parameters) welke
simpelweg de repository klasse instancieërt en in de private member plaatst. Doe
dit bij voorkeur via het doorlinken naar de eerdere genoemde Constructor met de parameter via de this statement.

Met deze eenvoudige uitbreiding op het MVC framework worden controllers
nog beter testbaar. Wil je dus weten of jouw controllers goed testbaar zijn?
Controleer dan de app.config van het unittest project. Daar mag niet de database
ConnectionString aan toegevoegd is.

Wil je eens starten met MVC2? Negeer de cheesy titel en bekijk eens Nerd
Dinner
. Dit geeft snel en goed een overzicht van wat MVC2 inhoudt.