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 18th, 2009 | 7 Comments
7 Responses to “ASP.NET MVC, TDD and AutoMapper” Leave your Comment
  1. Jon Kruger says:

    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.

  2. Richard says:

    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.

  3. Richard Dingwall » ASP.NET MVC, TDD and Fluent Validation says:

    [...] I posted about ASP.NET MVC, TDD and AutoMapper, and how you can use them together in a DDD application. Today I thought I would follow up with a [...]

  4. cowgaR says:

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

  5. cowgaR says:

    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

  6. Richard says:

    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.

  7. dario-g says:

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

Leave a reply

We love to hear your views.