Search code examples
c#moqclone

Unit Testing a clone method with Moq in C#


I have some basic classes with cloning methods:

public class SimpleClass
{
    public int ValueA { get; set; }

    public string ValueB { get; set; }

    public ulong ValueC { get; set; }

    public SimpleClass TypedClone()
    {
        var item = new SimpleClass
        {
            ValueA = this.ValueA,
            ValueB = this.ValueB,
            ValueC = this.ValueC 
        };

        return item;
    }
}

I want a unit test that will tell me if I add ValueD, but forget to add it to the Clone method. My first attempt was to use Moq and its VerifyGet method to make sure that each property was accessed.

    public void GenericCloneTest()
    {
        var mock = new Mock<SimpleClass>();
        var c = mock.Object.GenericClone();
        var properties = typeof(SimpleClass).GetProperties();

        foreach (var property in properties)
        {
            var expression = Expression.Property(
                Expression.Parameter(typeof(SimpleClass), "c"), 
                property);

            var type = Expression.GetFuncType(typeof (SimpleClass),  
                property.PropertyType);

            var actionExpression = Expression.Lambda(type, expression,
                Expression.Parameter(typeof(SimpleClass), "c"));

            mock.VerifyGet<object>
                ((Expression<Func<SimpleClass,object>>)actionExpression);
        }
    }

This doesn't work because the VerifyGet method needs to know the return type of the Property accessor, and I can't figure out any way to insert it at runtime (you'll notice my lame attempt to use "object" which crashed and burned).

I'm not even sure using Moq is a good idea, it was just my first one.

UPDATE: With no quick and easy generic way to test a cloning method I settled on writing type-specific tests for each class. This still leaves me with the problem of knowing when properties have been added. I settled on appending this to my clone unit tests:

      var signature = typeof (Connection)
            .GetProperties()
            .Select(p => p.Name)
            .Aggregate(
                new StringBuilder(), 
                (builder, name) =>
                    builder.Append(name)).ToString();

      Assert.AreEqual(
           "DataSessionStateDataTechnologyBytesReceivedBytesSentDuration",
           signature);

If I add a property the test will fail. It still depends on me being responsible enough to fix the rest of the test when the signature match fails.


Solution

  • One of the simplest ways to ensure you get all of your fields is to simply use the MemberwiseClone() method. That will automatically copy all of your classes fields to the new instance.