Showing posts with label AutoMapper. Show all posts
Showing posts with label AutoMapper. Show all posts

Thursday, 21 June 2012

Mocking AutoMapper in Unit Testing

This post will show how to mock AutoMapper with Moq in unit testing. Unit testing that has dependency to AutoMapper would require all of the mapping configurations be specified and run first before the actual mapping takes place (Mapper.Map(...) is called). These configurations would be burdensome and should not be included in unit testing. A unit test for a feature should only test that particular feature the developer has written. It should not test other services or functionalities. This is where the concept of mocking come up.

To be able to mock AutoMapper, we can use Dependency Injection to inject a mapper interface to the constructor of the calling code's class rather than using AutoMapper directly. Below is an example of such interface:
public interface IMappingService
{
    TDest Map<TSrc, TDest>(TSrc source) where TDest : class;
}

Then create an implementation class for the interface. Note on line 5 that for this class we specify AutoMapper directly.
public class MappingService : IMappingService
{
    public TDest Map<TSrc, TDest>(TSrc source) where TDest : class
    {
        return AutoMapper.Mapper.Map<TSrc, TDest>(source);
    }
}

Next, bind the interface with the concrete class. Below is an example of how to do it with Ninject:
kernel.Bind<IMappingService>().To<MappingService>();

Then whenever we want to do mapping, we call the interface's Map method instead of using the AutoMapper's Mapper.Map() method.
public ViewResult List()
{
    IEnumerable<Stuff> stuffs = stuffRepository.All;

    List<StuffViewModel> model = mappingService.Map<IEnumerable<Stuff>, List<StuffViewModel>>(stuffs);

    return View("List", model);
}
Please remember that mapping configurations need to be specified first before we could do any mapping. You can see this post to see how to set up the configurations.

Now we can mock the mapper in our unit test. In the example below I use xUnit testing framework. As you can see; first we create a mock instance from the interface, setup what the Map method will return and then pass the mock object to the class constructor of the feature to be tested (line 6, 25 and 28).
[Fact]
public void ListPageReturnsStuffViewModels()
{
    // Arrange
    Mock<IStuffRepository> stuffRepository = new Mock<IStuffRepository>();
    Mock<IMappingService> mappingService = new Mock<IMappingService>();

    List<Stuff> stuffs = new List<Stuff>();
    stuffRepository.Setup(r => r.All).Returns(stuffs.AsQueryable());

    var viewModelStuffs = new List<StuffViewModel> {
        new StuffViewModel { StuffID = 1/*,
                                Name= "Bip",
                                Description= "Colourful baby bip",
                                DateAdded = DateTime.Now,
                                UserID = 1 */
        },
        new StuffViewModel { StuffID = 2/*,
                                Name= "Socks",
                                Description= "Winter socks with animal figures",
                                DateAdded = DateTime.Now,
                                UserID = 1 */
        }
    };
    mappingService.Setup(m => m.Map<IEnumerable<Stuff>, List<StuffViewModel>>(It.IsAny<IEnumerable<Stuff>>()))
                    .Returns(viewModelStuffs);

    var controller = new StuffsController(stuffRepository.Object, mappingService.Object);


    // Act
    var result = controller.List() as ViewResult;
    //var model = result.ViewData.Model as List<StuffViewModel>;


    // Assert
    var model = Assert.IsType<List<StuffViewModel>>(result.ViewData.Model);
    Assert.Equal(2, model.Count);                
}

Friday, 11 November 2011

More Advanced Use of AutoMapper - Part 2

Custom type converters
// Source and destination classes
public class Source
{
    public string Value1 { get; set; }
    public string Value2 { get; set; }
    public string Value3 { get; set; }
}
public class Destination
{
    public int Value1 { get; set; }
    public DateTime Value2 { get; set; }
    public Boolean Value3 { get; set; }
}


// Custom type converter classes
  //custom type converter class uses 'ITypeConverter' interface and has 'Convert' method
public class DateTimeTypeConverter : ITypeConverter<string, DateTime>
{
    public DateTime Convert(ResolutionContext context)
    {
        return System.Convert.ToDateTime(context.SourceValue);
    }
}
public class TypeTypeConverter : ITypeConverter<string, Boolean>
{
    public Boolean Convert(ResolutionContext context)
    {
        return System.Convert.ToBoolean(context.SourceValue);
    }
}


// Specify mappings
  //using .NET built in function
Mapper.CreateMap<string, int>().ConvertUsing(Convert.ToInt32);

  //using custom type converter classes
Mapper.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());
Mapper.CreateMap<string, Boolean>().ConvertUsing<TypeTypeConverter>();

Mapper.CreateMap<Source, Destination>();


// Usage
var source = new Source
{
    Value1 = "5",
    Value2 = "01/01/2000",
    Value3 = "true"
};
Destination result = Mapper.Map<Source, Destination>(source);
With custom type converter, the mapping is applied automatically by AutoMapper when any source and destination types match. This mapping has a global scope.



Custom value resolvers
// Source and destination classes
public class SourceRsolvr
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}
public class DestinationRsolvr
{
    public int Total { get; set; }
}


// Custom resolver classes
  //custom resolver class derives from 'ValueResolver' and overrides 'ResolveCore' method, we can also create a
  //custom resolver class that derives from 'IValueResolver' but this is rarely used

  //a custom resolver without argument
public class CustomResolverOne : ValueResolver<SourceRsolvr, int>
{
    protected override int ResolveCore(SourceRsolvr source)
    {
        return source.Value1 + source.Value2;
    }
}

  //a custom resolver with argument
public class CustomResolverTwo : ValueResolver<SourceRsolvr, int>
{
    private readonly Expression<Func<int, bool>> _func;

    public CustomResolverTwo(Expression<Func<int, bool>> func)
    {
        _func = func;
    }

    protected override int ResolveCore(SourceRsolvr source)
    {
        var list = new[] { source.Value1, source.Value2};
        return list.Where(_func.Compile()).Sum();
    }
}


// Mapping
  // using custom resolver without argument
Mapper.CreateMap<SourceRsolvr, DestinationRsolvr>()
        .ForMember(dest => dest.Total, 
                   opt => opt.ResolveUsing<CustomResolverOne>()
                             .ConstructedBy(() => new CustomResolverOne()));

  // another example of using custom resolver with argument
//Mapper.CreateMap<SourceRsolvr, DestinationRsolvr>()
//        .ForMember(dest => dest.Total, 
//                   opt => opt.ResolveUsing<CustomResolverTwo>()
//                             .ConstructedBy(() => new CustomResolverTwo(x => x > 5)));


// Usage
var source = new SourceRsolvr
{
    Value1 = 5,
    Value2 = 7
};
var result = Mapper.Map<SourceRsolvr, DestinationRsolvr>(source);
Unlike custom type converter, custom value resolver needs to be specified in the configuration of any destination class' member that would like to apply it. Thus it will only be implemented to specific members that are configured for it.

Reference:
https://github.com/AutoMapper/AutoMapper/wiki

Friday, 4 November 2011

More Advanced Use of AutoMapper - Part 1

Projection
public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
}

public class PersonViewModel
{
    public string FullName { get; set; }
    public int DayOfBirth { get; set; }
    public int MonthOfBirth { get; set; }
    public int YearOfBirth { get; set; }
    public string Email { get; set; }
}


Mapper.CreateMap<Person, PersonViewModel>()
    .ForMember(d => d.DayOfBirth, o => o.MapFrom(s => s.DateOfBirth.Day))
    .ForMember(d => d.MonthOfBirth, o => o.MapFrom(s => s.DateOfBirth.Month))
    .ForMember(d => d.YearOfBirth, o => o.MapFrom(s => s.DateOfBirth.Year))
    .ForMember(d => d.FullName, o => o.MapFrom(s => s.FirstName + " " + s.LastName))
    .ForMember(d => d.Email, o => o.Ignore());
AutoMapper needs to know how to map to ALL of the destination class members. It is fine if some source class' members do not have any match. In this case the source class Person has a PersonId member, however it is not used in the destination class. No mapping configuration is required, AutoMapper will automatically ignore it. However for the destination class member Email which do not have a match in the source class, it is necessary to specify a mapping configuration.


Collections
After a map is created, we can map a collection of source class' objects into a collection of destination class' objects without extra configuration.
var sources = new[]
    {
        new Person{PersonId =10, DateOfBirth=new DateTime(1910,10,1), FirstName="Johnny", LastName="King"},
        new Person{PersonId =11, DateOfBirth=new DateTime(1911,11,2), FirstName="Katherine", LastName="Wood"},
        new Person{PersonId =12, DateOfBirth=new DateTime(1912,12,3), FirstName="Sam", LastName="Bourke"}
    };
List<PersonViewModel> listPersonViewModels = Mapper.Map<Person[], List<PersonViewModel>>(sources);
Collection types supported are: IEnumerable, IEnumerable<T>, ICollection, ICollection<T>, IList, IList<T>, List<T> and Arrays.


Nested mappings
No mapping configuration is needed to map a nested class. As long as all of the destination class' members have matches, we just need to specify one more mapping for each nested class.
public class Outer
{
    public int Value { get; set; }
    public Nested InnerClass { get; set; }
}
public class Nested
{
    public int InnerValue { get; set; }
}

public class OuterViewModel
{
    public int Value { get; set; }
    public NestedViewModel InnerClass { get; set; }
}
public class NestedViewModel
{
    public int InnerValue { get; set; }
}


Mapper.CreateMap<Outer, OuterViewModel>();
// need to specify mapping for the nested class as well
Mapper.CreateMap<Nested, NestedViewModel>();


// usage
var source = new Outer
{
    Value = 5,
    InnerClass = new Nested { InnerValue = 15 }
};
var dest = Mapper.Map<Outer, OuterViewModel>(source);

Reference:
https://github.com/AutoMapper/AutoMapper/wiki

Tuesday, 25 October 2011

Using View Model with AutoMapper in MVC

Tools used when writing this post: MVC3, MvcScaffolding 1.0.0, EntityFramework 4.1.10331.0 and AutoMapper 2.0.0.

In this post, we will see a simple example of how to map a view model to a model class with AutoMapper. To understand more about view model, you may want to read http://rachelappel.com/use-viewmodels-to-manage-data-amp-organize-code-in-asp.net-mvc-applications. For more information about AutoMapper, see "AutoMapper - Getting Started".

Say we have a model class called Arena that is used by a database context class for Entity Framework. Then we have a view model for this class called ArenaViewModel that is created to handle slightly different form validation requirements from the original model class. Below are the model class and its view model:
public class Arena
{
    public int ArenaId { get; set; }
        
    [StringLength(150)]
    public string Name { get; set; }

    [StringLength(350)]
    public string Location { get; set; }
        
    public int NumberOfSeats { get; set; }
}

public class ArenaViewModel
{
    public int ArenaId { get; set; }

    //input requirement: field is required and can only have maximum 10 characters length 
    [Required]
    [StringLength(10)]
    public string Name { get; set; }

    //input requirement: field is required and can only have maximum 10 characters length
    [Required]
    [StringLength(10)]
    public string Location { get; set; }
                
    //input requirement: allow empty value (as this will be translated to 0 when saving to database)
    public int? NumberOfSeats { get; set; }
}

Then to set up AutoMapper, first copy and paste the class below to our project. Specify the mapping profile with a correct profile class name that we are going to create (line 6).
public class AutoMapperConfiguration
{
    public static void Configure()
    {
        // specify the mapping profile
        Mapper.Initialize(x => x.AddProfile<ViewModelProfile>());

        // Put this in unit testing later!
        // verify mappings
        Mapper.AssertConfigurationIsValid();
    }
}

Next, create the profile class. A profile class is used to centralized many mapping configurations in one place. A different profile can have different mapping configurations and formatting rules of similar entities in the other profile.
public class ViewModelProfile : Profile
{
    public override string ProfileName
    {
        get { return "ViewModel"; }
    }

    protected override void Configure()
    {
        // specify all mapping configurations here

        CreateMap<ArenaViewModel, Arena>();
    }
}
Note that this class is derived from Profile base class.

Then call
AutoMapperConfiguration.Configure();
from Application_Start() method on Global.asax.cs.

Say we already have a view called AddArenaInfo.cshtml that uses ArenaViewModel to add a new arena information:
@model MvcScaffoldTest.ViewModels.ArenaViewModel
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    <div class="editor-label">
        @Html.LabelFor(model => model.Name)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Name)
        @Html.ValidationMessageFor(model => model.Name)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.Location)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Location)
        @Html.ValidationMessageFor(model => model.Location)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.NumberOfSeats)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.NumberOfSeats)
        @Html.ValidationMessageFor(model => model.NumberOfSeats)
    </div>

    <p>
        <input type="submit" value="Create" />
    </p>
}

Then we would be able to do this on our controller:
[HttpPost]
public ActionResult AddArenaInfo(ArenaViewModel arenaVwMdl)
{
    if (ModelState.IsValid)
    {
        //map source to destination
        var arenaMdl = Mapper.Map<ArenaViewModel, Arena>(arenaVwMdl);
        context.Arenas.Add(arenaMdl);
        context.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(arenaVwMdl);
}
AutoMapper will map those two objects automatically. We don't need to specify extra mapping configurations in this case because both of the classes have similar properties that can be easily matched by AutoMapper.

References:
http://mhinze.com/2009/07/06/automapper-in-nerddinner/
http://elegantcode.com/2009/10/06/automapper-introduction-and-samples-from-netdug/