draptik

mostly tech stuff

Testing That Different Objects Have the Same Properties

Sometimes you want to ensure that 2 unrelated objects share a set of properties — without using an interface.

Here is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace Demo
{
    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}

First thought for C# developers: AutoMapper

Let’s do that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using AutoMapper;

namespace Demo
{
    public class MyMapping
    {
        public static IMapper Mapper;

        public static void Init()
        {
            var cfg = new MapperConfiguration(x =>
            {
                x.CreateMap<Customer, Person>();
            });
            Mapper = cfg.CreateMapper();
        }
    }
}

Now we can write a unit test to see if we can convert a Customer to a Person:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using Xunit;

namespace Demo
{
    public class SomeTests
    {
        [Fact]
        public void Given_Customer_Should_ConvertTo_Person()
        {
            // Arrange
            const string firstname = "foo";
            const string lastname = "bar";

            var customer = new Customer
            {
                FirstName = firstname,
                LastName = lastname
            };

            MyMapping.Init();

            // Act
            var person = MyMapping.Mapper.Map<Customer, Person>(customer);

            // Assert
            person.FirstName.Should().Be(firstname);
            person.LastName.Should().Be(lastname);
        }
  }
}  

This test passes.

But what happens when we want to ensure that a new Customer property (for example Email) is reflected in the Person object?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace Demo
{
    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; } // <-- new property
    }

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}

Our unit test still passes. ☹

Wouldn’t it be nice to have our unit test fail if the classes are not in sync?

Here is where FluentAssertions ShouldBeEquivalentTo comes in handy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using FluentAssertions;
using Xunit;

[Fact]
public void Given_Customer_Should_ConvertTo_Person_With_CurrentProperties()
{
    //Arrange
    const string firstname = "foo";
    const string lastname = "bar";

    var customer = new Customer
    {
        FirstName = firstname,
        LastName = lastname,
        Email = "foo@bar.com"
    };

    MyMapping.Init();

    // Act
    var person = MyMapping.Mapper.Map<Customer, Person>(customer);

    // Assert
    customer.ShouldBeEquivalentTo(person);
}

Subject has a member Email that the other object does not have.

Cool: This is the kind of message I want to have from a unit test!

ShouldBeEquivalentTo also takes an optional Lambda expression in case you need more fine grained control which properties are included in the comparison. Here is an example where we exlude the Email property on purpose:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using FluentAssertions;
using Xunit;

[Fact]
public void Given_Customer_Should_ConvertTo_Person_With_CurrentProperties_Excluding_Email()
{
    //Arrange
    const string firstname = "foo";
    const string lastname = "bar";

    var customer = new Customer
    {
        FirstName = firstname,
        LastName = lastname,
        Email = "foo@bar.com"
    };

    MyMapping.Init();

    // Act
    var person = MyMapping.Mapper.Map<Customer, Person>(customer);

    // Assert
    customer.ShouldBeEquivalentTo(person,
        options =>
            options.Excluding(x => x.Email));
}

This test passes.

The complete documentation for FluentAssertions’ ShouldBeEquivalentTo method can be found here.

Source code for this post

You can clone a copy of this project here: https://github.com/draptik/blog-demo-shouldbeequivalentto.

1
git clone https://github.com/draptik/blog-demo-shouldbeequivalentto.git

Comments