.NET MVC, Unit testing, databuilders and dependency injection (part 2)

Tags: C# MVC

This post is the second part in a series. For this first part, please check out this post.

The purpose of this series is to show you some of the lessons I learned when I started using MVC, Entity Framework, dependency injection, unit testing and various design patterns. As with all new technologies, patterns and ideas, it takes a while to get comfortable with it and learn how to apply them in a good way. I believe that this is in part because tutorials often focus on the details of a technology or a pattern. Putting everything together is often left up to the reader. In this series of posts, I want to show you an application that I wrote for the purposes of this series that combines Entity Framework (6), MVC (4), unit testing, dependency injection with Ninject, Bootstrap, AppHarbor (continuous delivery) and repository patterns. 

You can download the sourcecode from BitBucket. Please check the previous post for instructions on how to get everything running on your deskop. A live deployed version of the sourcecode van be checked here.

Entity Framework: Injection of dependencies with Ninject into controllers

Throughout the application, you’ll notice that several classes have constructors that require a number of dependencies to be specified. Take the ApplicationRepository class:

public ApplicationRepository(IDataContext dataContext) { .... }

Or the ApplicationController:

public class ApplicationController : ControllerBase
{
     public ApplicationController(IApplicationRepository applicationRepository, ITeamRepository teamRepository)
     {        
          .....
     }

     .....
}

This is an example of injection of dependencies through constructor injection (the most common form). The purpose of injection is to decrease dependencies between classes. In the above example, the ApplicationController is apparently going to do something with the ApplicationRepository class. Instead of creating an instance of the ApplicationRepository within the ApplicationController (e.g. var repo = new ApplicationRepository()), the repository is injected through the constructor instead. This reduces coupling between classes and is very useful when writing unit tests for your classes. When writing unit tests, you can inject a ‘mocked’ version of the class and control all it’s behavior (more on that below). In my case, I’m using the Ninject framework to do the injection for me. If you wish to read more about why this is important, please check out my post on Ninject. In my controllers, the repositories are injected through the constructor:

   public class ApplicationController : ControllerBase
    {
        internal readonly IApplicationRepository applicationRepository;
        internal readonly ITeamRepository teamRepository;

        public ApplicationController(IApplicationRepository applicationRepository, ITeamRepository teamRepository)
        {
            this.applicationRepository = applicationRepository;
            this.teamRepository = teamRepository;
        }

        .....
   }

This works automatically once you install the Ninject.MVC3 NuGet package for an MVC3+ site. Ninject injects itself into the .NET pipeline in such a way that all controllers are created through Ninject and dependencies are automatically located and passed in. This requires only some configuration in the NinjectWebCommon.cs file in the AppStart_ folder (created when the package is installed):

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IDataContext>().To<DataContext>();
        kernel.Bind<IApplicationRepository>().To<ApplicationRepository>();
        kernel.Bind<ITeamRepository>().To<TeamRepository>();
   }

Ninject is clever enough to cascade the injection. When it injects IApplicationRepository into the ApplicationController constructor, it will notice that ApplicationRepository itself requires an instance of IDataContext and inject that as well. This chain of dependencies is performed by Ninject automatically. Except for the boilerplate code in the AppStart_ folder, the rest of the code is not specifically designed for Ninject and does not depend on it. 

Further down below I will show some examples of actual unit tests so you can also see why this is so incredibly useful.

MVC: Unit testing

The MVC framework makes it easy to unit test your controllers. Now, I don’t normally unit test my controllers completely. Instead, I extract important logic into separate classes and write tests for them. This keeps my controllers lean and mean and shifts the focus of my unit tests to these business logic classes. I usually find that writing unit tests for controller actions in this scenario doesn’t really add a lot or prevent a lot of bugs. It’s mostly a matter of spending my time on the most important kinds of tests. TDD proponents may not like this approach, but it’s not always practical to achieve 100% test coverage :)

In the example code, I have not extracted the business logic into it’s own classes for the sake of the example, so I have written a number of unit tests for the controller actions themselves.

I have written unit tests for my repositories (Repositories.Tests project) and for my website (Website.Tests project). A simple example of one of the unit tests is this one:

[TestMethod]
public void Index_returns_list_of_all_applications_ordered_by_name()
{
    // setup;
    applicationRepository.Setup(p => p.GetAll()).Returns(new List<Application>() { 
        new ApplicationBuilder().WithName("B").Build(),
        new ApplicationBuilder().WithName("A").Build()});

    // act
    var controller = CreateInstance();
    ViewResult model = (ViewResult)controller.Index();
    var result = (ApplicationListViewModel)model.Model;

    // verify
    Assert.AreEqual(2, result.Applications.Count);
    Assert.AreEqual("A", result.Applications[0].Name);
    Assert.AreEqual("B", result.Applications[1].Name);
}

This unit test tests the Index action on the ApplicationController. It does pretty much what the name of the test says; it checks if the Index action returns a viewmodel with a list of applications, ordered by name. It begins by setting up the ApplicationRepository to return a list of (unordered) applications. It then creates an instance of the controller itself and verifies the returned value.

private ApplicationController CreateInstance()
{
     return new ApplicationController(applicationRepository.Object, teamRepository.Object);
}

The CreateInstance helper method returns an instance of ApplicationController where the two dependencies are injected with mocked instances. In this particular case, I have instructed my mocked ApplicationRepository to return a list of applications when GetAll() is called. So, I’m not relying on any code I’ve writting in the actual ApplicationRepository class that lives in my Repositories project. This is a good example of using dependency injection for unit testing.

The other unit tests can be found in the code. I’ve written a few dozen for the controllers. You can run them, check their results and play with them a little bit.

Unit testing repositories

I’ve also written unit tests for my Repositories. If you wish to read more about how I wrote this code, please check the previous post. Let’s take this test as an example:

[TestMethod]
public void GetAll_does_not_return_soft_deleted_entities()
{
    // setup
    var entity1 = new TestEntity() { Name = "A", Deleted = DateTime.Now };
    fakeDataContext.Set<TestEntity>().Add(entity1);

    // act
    var results = CreateInstance().GetAll();

    // verify
    Assert.AreEqual(0, results.Count());
}

In the test, I’m testing if the GetAll() method on the generic repository (RepositoryBase) does not return (soft)deleted records. So, first I set up an instance of TestEntity and mark it as deleted by setting the ‘Deleted’ property to today’s date. I add this entity to an internal list of a fake DataContext that I wrote specifically for testing purposes. I then create an instance of the repository (through the helper method CreateInstance()), call the GetAll() method and check if the method does not return any records. The only thing we’re basically checking here, is if the GetAll() method filters out deleted records.

When you look at the repository tests, you'll notice a few other things:

  • Because my TeamRepository and ApplicationRepository don't have any custom methods, I only wrote tests for my base repository (RepositoryBase.cs) class. And because this is an abstract class that I cannot instance directly, I chose to create a TestRepository class for a test entity (TestEntity) that lives in my test project. This way, I don't have to tie my RepositoryBase tests to a particular domain entity and can re-use the repositories and tests in other projects without change;
  • I am not a big fan of writing unit tests for my repositories, unless there is important logic in there (like the soft deletion pattern in this case). The problem with testing repositories is that you need some kind of fake datacontext to avoid hitting a real database and manipulate test scenarios. This is tricky because it is tempting to write tests that essentially test only your test code (the fake datacontext) or leak SQL connectivity;

Unit testing: Databuilders

I like the DataBuilders pattern. My colleague William recently made me aware of this useful design pattern, and I've been loving it ever since. This is an example of a databuilder:

var application = new ApplicationBuilder().WithName("A").WithDefaultTeam().Build();

This builder creates an instance of Application pre-filled with A as name and with a default Team assigned to it. The builder code itself is really straightforward:

public class ApplicationBuilder
{
    private int id;
    private string name;
    private DateTime? deleted = null;
    private Team team;

    public ApplicationBuilder()
    {
        this.id = RandomNumber.Get();
        this.team = new Team();
    }

    public static implicit operator Application(ApplicationBuilder builder)
    {
        return builder.Build();
    }

    public Application Build()
    {
        return new Application
        {
            Id = id,
            Name = name,
            Deleted = deleted,
            TeamId = team.Id,
            Team = team
        };
    }

    public ApplicationBuilder WithName(string name)
    {
        this.name = name;
        return this;
    }
    public ApplicationBuilder WithDefaultTeam()
    {
        this.team = new TeamBuilder().WithName("Team").Build();
        return this;
    }

    public ApplicationBuilder WithTeam(Team team)
    {
        this.team = team;
        return this;
    }
}

When setting up builders, I only create With[Property] methods for properties that I actually need when testing. All the other properties I either leave empty or I populate them with default or random values. For more complicated scenarios, I create specific With[Scenario] methods. The WithDefaultTeam is a simple example of this.

MVC: Viewmodels and data mapping

When working with MVC, the model is the data that is interchanged between the controller and the view. It is tempting to just use the domain entities directly (e.g. Application or Team) here. So, if I'm showing a list of applications, the controller simply passes a list of application instances to the view:

public ActionResult Index()
{
     var viewModel = new ApplicationListViewModel();
     var applications = applicationRepository.GetAll().OrderBy(p => p.Name);
     return View(applications);
}

Although this will certainly work, you are effectively tying your views to your data model. This may not be a problem for smaller web applications, but will cause problems down the road if your data model changes and grows. So, I always create viewmodels. These classes are tailored towards specific views. In the example application, there are three viewmodels for the application views:

  • ApplicationViewModel: Used for viewing a single application and used as a basis for the other viewmodels (for the time being). This model also contains validation attributes, but they could be overridden by children of this class for fine-tuning;
  • ApplicationListViewModel: Used for viewing a list of applications. This model currently only contains a list of ApplicationViewModels, but it can be extended with paging parameters (e.g. pagesize, pageindex) down the road;
  • ApplicationManageViewModel: Used for managing applications. This model currently inherits from ApplicationViewModel because it shares the properties. But it is easy to override properties with other validation attributes;

The ApplicationViewModel looks like this:

public class ApplicationViewModel
{
    public ApplicationViewModel()
    {
    }

    public ApplicationViewModel(Application application)
    {
        this.Id = application.Id;0
        this.Name = application.Name;
        this.Description = application.Description;
        this.TeamName = application.Team != null ? application.Team.Name : string.Empty;
        this.TeamId = application.TeamId;
    }

    public int Id { get; set; }
    [Required(ErrorMessage = "Name is required")]
    public string Name { get; set; }
    public string TeamName { get; set; }
    public string Description { get; set; }
    [Required(ErrorMessage = "Team is required")]
    public int TeamId { get; set; }
}

Note the additional constructor that takes a single application. In addition to transferring data between the layers, the viewmodel can also populate it's own properties based on a single application instance, effectively taking this responsibility away from the controllers.

The advantage of this approach that I can put this mapping code (Application > ViewModel) in one class and re-use it. This also makes it easy to unit test this code once:

[TestMethod]
public void Constructor_populates_viewmodel_with_application_properties_if_one_is_supplied()
{
    // setup
    var application = new ApplicationBuilder().WithName("Name").WithDefaultTeam().Build();
    // act
    var model = new ApplicationViewModel(application);
    // verify
    Assert.AreEqual(application.Id, model.Id);
    Assert.AreEqual(application.Name, model.Name);
    Assert.AreEqual(application.Description, model.Description);
    Assert.AreEqual(application.TeamId, model.TeamId);
    Assert.AreEqual(application.Team.Name, model.TeamName);
}

A good example of why this approach works well is shown below, for the ApplicationListViewModel:

public class ApplicationListViewModel
{
    public ApplicationListViewModel()
    {
        this.Applications = new List<ApplicationViewModel>();
    }
    public ApplicationListViewModel(IEnumerable<Application> applications)
    {
        this.Applications = new List<ApplicationViewModel>();

        foreach (var application in applications)
        {
            this.Applications.Add(new ApplicationViewModel(application));
        }
    }
    public List<ApplicationViewModel> Applications { get; set; }
}

Because the ApplicationListViewModel contains a list of ApplicationViewModels, adding new items to that list is now as easy as this:

this.Applications.Add(new ApplicationViewModel(application))

In the Website.Tests project, you'll find a unit test for this viewmodel as well.

I've done something similar for the ApplicationManageViewModel:

public class ApplicationManageViewModel : ApplicationViewModel
{
    public ApplicationManageViewModel()
        : base()
    {
    }
    public ApplicationManageViewModel(Application application)
        : base(application)
    {
    }
    public Application ToDalEntity()
    {
        return ToDalEntity(new Application());
    }

    public Application ToDalEntity(Application application)
    {
        application.Id = this.Id;
        application.Name = this.Name;
        application.Description = this.Description;
        application.TeamId = this.TeamId;
        return application;
    }
}

This viewmodel has a ToDalEntity method with an overload. It takes a new or an existing instance of an application and populates it with values from the viewmodel. This is useful in the controllers, when creating or updating entities through the repositories:

var application = model.ToDalEntity();
applicationRepository.InsertAndSubmit(application);

Again, I can put this logic in one place and use it from several actions (Create and Edit). I can write a single unit test for it and be sure that the mapping of data from the viewmodel to the domain entity works:

[TestMethod]
public void ToDalEntity_converts_model_properties_to_DAL_entity()
{
    // setup
    var application = new ApplicationBuilder().WithName("Name").WithDefaultTeam().Build();
    var model = new ApplicationManageViewModel(application);
    // act
    var result = model.ToDalEntity();
    // verify
    Assert.AreEqual(application.Id, result.Id);
    Assert.AreEqual(application.Name, result.Name);
    Assert.AreEqual(application.Description, result.Description);
    Assert.AreEqual(application.TeamId, result.TeamId);
}

The downside of viewmodels is that they require quite a bit of extra code. Just passing the domain entities also works, after all. On the other hand, viewmodels are easy to write, make your code more readable and can extract some logic from your controllers. Even for this small application, they prevented some code duplication.

Concluding thoughts

In this post, I've shown you how to write unit tests for an MVC (4) project and for your repositories. I've also shown how to use DataBuilders to make setting up your test data easier, and I've shown you how dependency injection can be used (with Ninject). In the next post, I will show you how I used AppHarbor to continuously deliver a new version (integrate, test and publish it) after committing new changes to BitBucket.

Christiaan Verwijs
Christiaan Verwijs

Scrum Master, Trainer, Developer & founder of Agilistic