ASP.NET MVC, TDD and AutoMapper

This post is in response to a question on a recent article I wrote about mapping domain entities to presentation models with AutoMapper, an object-object mapper for .NET. Today I will give a brief example of how we can tie it all together in an ASP.NET MVC application using dependency injection and application services.

First, let’s start with the controller and the application service it talks to:

public class TasksController : Controller
{
    readonly ITaskService tasks;

    public TasksController(ITaskService tasks)
    {
        if (tasks == null)
            throw new ArgumentNullException("tasks");

        this.tasks = tasks;
    }

    public ActionResult Index()
    {
        IEnumerable<TaskView> results = this.tasks.GetCurrentTasks();
        return View(results);
    }
    
    ...
}
public interface ITaskService
{
    IEnumerable<TaskView> GetCurrentTasks();
    TaskView AddTask(TaskForm task);
    TaskView SaveTask(TaskForm task);
    void DeleteTask(int id);
}

Note the service’s inputs and outputs are defined in terms of view models (TaskView) and edit models (TaskForm). Performing this mapping in the application services layer keeps our controllers nice and simple. Remember we want to keep them as thin as possible.

Inside the tasks service

public class TaskService : ITaskService
{
    readonly ITaskRepository taskRepository;
    readonly IMappingEngine mapper;

    public TaskService(ITaskRepository taskRepository, IMappingEngine mapper)
    {
        if (taskRepository == null)
            throw new ArgumentNullException("taskRepository");

        if (mapper == null)
            throw new ArgumentNullException("mapper");

        this.taskRepository = taskRepository;
        this.mapper = mapper;
    }

    public IEnumerable<TaskView> GetCurrentTasks()
    {
        IEnumerable<Task> tasks = this.taskRepository.GetAll();
        return tasks.Select(t => this.mapper.Map<Task, TaskView>(t));
    }
    
    ...
}

The tasks service has two dependencies: the tasks repository* and AutoMapper itself. Injecting a repository into a service is simple enough, but for AutoMapper we have to inject an IMappingEngine instance to break the static dependency on AutoMapper.Mapper as discussed in this post.

* Note this is a very simple example — in a bigger app we might use CQS instead of querying the repository directly.

Testing the service

We are using Moq to isolate the tasks service from its repository and AutoMapper dependencies, which always return a known result from the Object Mother. Here are our test cases for all the different things that should occur when retrieving the current tasks:

[TestFixture]
public class When_getting_all_current_tasks
{
    Mock<ITaskRepository> repository;
    Mock<IMappingEngine> mapper;
    ITaskService service;
    IEnumerable<Task> tasks;

    [SetUp]
    public void SetUp()
    {
        repository = new Mock<ITaskRepository>();
        mapper = new Mock<IMappingEngine>();

        service = new TaskService(repository.Object, mapper.Object);

        tasks = ObjectMother.GetListOfTasks();
        repository.Setup(r => r.GetAll()).Returns(tasks);

        mapper.Setup(m => m.Map<Task, TaskView>(It.IsAny<Task>()))
            .Returns(ObjectMother.GetTaskView());
    }

    [Test]
    public void Should_get_all_the_tasks_from_the_repository()
    {
        service.GetCurrentTasks();

        repository.Verify(r => r.GetAll());
    }

    [Test]
    public void Should_map_tasks_to_view_models()
    {
        service.GetCurrentTasks();

        foreach (Task task in tasks)
            mapper.Verify(m => m.Map<Task, TaskView>(task));
    }

    [Test]
    public void Should_return_mapped_tasks()
    {
        IEnumerable<TaskView> results = service.GetCurrentTasks();
        results.Should().Not.Be.Empty();
    }
}

Enter AutoMapper

As you can see, we have both the controller and service under test without needing to involve AutoMapper yet. Remember it is being tested separately as discussed in my previous post.

To wire up AutoMapper so it gets injected into the TaskService, all we have to do is register IMappingEngine in the IoC container:

container.RegisterInstance<IMappingEngine>(Mapper.Engine);

Putting the mapping step in the application service and then mocking out AutoMapper like this allows us to easily test everything in isolation, without having to set up the mapper first.

I hope this answers your question Paul!

August 18, 2009

12 Comments

Jon Kruger on August 19, 2009 at 5:45 am.

I see what you’re doing with mocking the IMappingEngine… I actually wired up StructureMap to actually use the real IMappingEngine in my unit tests. I feel like it’s made my tests much easier to read and write because I don’t have to write a separate test for the translation and another test for the thing that uses the translation.

Not to say what you’re doing it wrong though, by any means.

Richard on August 19, 2009 at 10:04 am.

John,

We originally started doing it like that, but found it became a pain to set up when the class mapping in question used custom IValueResolvers and/or ITypeConverters. It was simpler to mock out the entire automapper than all the individual dependencies.

cowgaR on August 23, 2009 at 10:16 am.

TaskServiceImpl should be TaskService, you probably forgot to rename a constructor…

cowgaR on August 23, 2009 at 10:43 am.

shouldn’t service.GetCurrentTasks(); be:
this.tasks = service.GetCurrentTasks();

and I have a problem with:
repository.Verify(r => r.GetAll());

Care to elaborate what MOQ will do? Can’t get this test at all :D

Richard on August 23, 2009 at 11:08 am.

cowgar,

1. TaskServiceImpl was a typo, nice spotting :)

2. I generally give repositories and services short names when they are injected into other classes, e.g. calling the ITaskService simply ‘tasks’. Generally that is all that is required to explain its purpose within the context of the caller.

3. It is to assert that TaskService.GetCurrentTasks() performs a call to this method from the repository:

public interface ITaskRepository
{
    IEnumerable<Task> GetAll();
}

Sorry I didn’t show the ITaskRepository as part of my example.

dario-g on September 15, 2009 at 12:02 pm.

If you use NHibernate then AutoMapping collections are done by AliasToBeanTransformer :)

alexanderb on September 28, 2010 at 6:05 pm.

This is a great article, exactly what I was looking for :)

Humberto Franco on September 28, 2010 at 8:46 pm.

Hi Richard,

How do you go about initializing your automapper configuration? You know doing this below… sorry for the newbie question.
Mapper.Initialize(cfg => cfg.AddProfile());
public class MyProfile : Profile
{
protected override void Configure()
{
CreateMaps();
}

private static void CreateMaps()
{
Mapper.CreateMap();
}
}

Jack on July 19, 2012 at 8:15 pm.

I’m late to this game but I’m losing the point of AutoMapper (for my models to view models) if you end up having to mock the mapping to get the tests to run. Having to write mocks becomes nearly identical to the work of actually writing the mapping manually.

Albert on February 20, 2014 at 10:32 am.

Richard,

I have couple of question about the architecture.
TaskView is a part of presentation, but TaskService method returns this type?
ITaskService looks very similar repository, it just maps between domain-model objects to DTO. Is any additional responsibility of a Service?

Thanks.

Leave a Reply