Click here to Skip to main content
Click here to Skip to main content

Using ClassAssert to check property mappings between different object types

, 30 May 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Using ClassAssert to check property mappings between different object types

Introduction

When developing modern business applications, a common architecture includes multiple layers. At the bottom of the stack is the database layer, either communicating directly with the database or alternatively using an Object Relational Mapping (ORM) framework such as Nhibernate or Entity Framework.

The one thing that we don't want to do is to allow parts of our application further up the chain to directly access database objects.

Why? Simply put, because there may be properties or methods within the database objects that should not be accessible outside of the database layer.

Commonly, we use Data Transfer Objects (DTOs) mapped to the original database objects with only the properties that you want to expose. However, this brings with it an additional process whereby each database object needs to be mapped to the relevant DTO(s) and vice versa.

Following the principles of Test Driven Development, we need to test these mappings.

Mapping a Database Object to its DTO

Let's look at the following database object:

public class Customer
{
    public int Id { get; set; }
    public string Forename { get; set; }
    public string Surname { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string Address3 { get; set; }
    public string Address4 { get; set; }
    public string Postcode { get; set; }
} 

And here is the DTO that it's going to be mapped to:

public class CustomerDto
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string Address3 { get; set; }
    public string Address4 { get; set; }
    public string Postcode { get; set; }
}

We're going to use AutoMapper to create the mapping between the two objects.

The Forename and Surname properties have different names in the DTO, so we need to explicitly map those. Everything else will be mapped automatically.

using AutoMapper; 
using SampleApp.Dtos; 
using SampleApp.Entities; 

public static class AutoMapperConfiguration
{
    public static void ConfigureMappings()
    {
        Mapper.CreateMap<Customer, CustomerDto>()
            .ForMember(dest => dest.FirstName, opts => opts.MapFrom(source => source.Forename))
            .ForMember(dest => dest.LastName, opts => opts.MapFrom(source => source.Surname));
    }
} 

Performing Unit Tests

So in this case, the Customer class will be mapped to CustomerDto. When we write our unit tests, we need to check that every property has been mapped correctly.

using AutoMapper; 
using NUnit.Framework; 
using Ploeh.AutoFixture; 

[TestFixture]
public class AutoMapperConfigurationTests
{
    private Fixture _fixture;
    [TestFixtureSetUp]
    public void TestFixtureSetUp()
    {
        _fixture = new Fixture();
        AutoMapperConfiguration.ConfigureMappings();
    }
    [Test]
    public void UseAssertToVerifyThatMappingsAreConfiguredBetweenCustomerAndCustomerDtoObjects()
    {
        // Arrange
        var source = _fixture.Create<Customer>();
        // Act
        var result = Mapper.Map<CustomerDto>(source);
        // Assert
        Assert.AreEqual(source.Forename, result.FirstName);
        Assert.AreEqual(source.Surname, result.LastName);
        Assert.AreEqual(source.Address1, result.Address1);
        Assert.AreEqual(source.Address2, result.Address2);
        Assert.AreEqual(source.Address3, result.Address3);
        Assert.AreEqual(source.Address4, result.Address4);
        Assert.AreEqual(source.Postcode, result.Postcode);
        Assert.AreEqual(source.DateOfBirth, result.DateOfBirth);
    }
}

Long winded, right? And what if we add another property to the CustomerDto object and have forgotten (yes, it's bad practice) to update the test?

All tests will still pass and everyone is blissfully unaware that the new property isn't mapped. This is where ClassAssert can help.

using System; 
using System.Linq;
using NUnit.Framework;
 
public static class ClassAssert
{
    public static void PropertiesAreEqual(object source, object target, string[] propertiesToIgnore = null)
    {
        foreach (var targetProperty in target.GetType().GetProperties())
        {
            if (propertiesToIgnore == null || !propertiesToIgnore.Contains(targetProperty.Name))
            {
                var sourceProperty = source.GetType().GetProperty(targetProperty.Name);
                if (sourceProperty != null)
                {
                    Assert.AreEqual(targetProperty.GetValue(target), sourceProperty.GetValue(source));
                }
                else
                {
                    throw new MemberAccessException(string.Format
                    ("Property '{0}' not found on source object.", targetProperty.Name));
                }
            }
        }
    }
}

Using ClassAssert

Using ClassAssert allows us to rewrite our unit test like this:

using AutoMapper; 
using NUnit.Framework; 
using Ploeh.AutoFixture;
 
[TestFixture]
public class AutoMapperConfigurationTests
{
    private Fixture _fixture;
    [TestFixtureSetUp]
    public void TestFixtureSetUp()
    {
        _fixture = new Fixture();
        AutoMapperConfiguration.ConfigureMappings();
    }
    [Test]
    public void UseClassAssertToVerifyThatMappingsAreConfiguredBetweenCustomerAndCustomerDtoObjects()
    {
        // Arrange
        var ignoredProperties = new[] { "FirstName", "LastName" };
        var source = _fixture.Create<Customer>();
        // Act
        var result = Mapper.Map<CustomerDto>(source);
        // Assert
        ClassAssert.PropertiesAreEqual(source, result, ignoredProperties);
        Assert.AreEqual(source.Forename, result.FirstName);
        Assert.AreEqual(source.Surname, result.LastName);
    }
}

Fewer lines, cleaner code and if any object is changed, the unit test includes their properties automatically.

As shown in the example, there are times when property names don't match up. We can cater to this by passing in the optional array of property names to ignore. You can then test those mappings in the usual way.

History

Originally published by Brent Jenkins at http://www.anterec.co.uk/articles/using-classassert-to-check-property-mappings-between-different-object-types/.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Brent Jenkins
Software Developer Anterec
United Kingdom United Kingdom
No Biography provided
Follow on   Twitter

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.141015.1 | Last Updated 30 May 2014
Article Copyright 2014 by Brent Jenkins
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid