MichaC
MichaC

Reputation: 2844

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.

Upvotes: 2

Views: 2799

Answers (2)

James Avery
James Avery

Reputation: 3012

You don't really want to use Moq here, you just need to use some reflection. Basically write a test that create an instance of your object, sets each property, clones it, and then makes sure that each property is equal on the clone.

Like Paul said, check out MemberwiseClone() that basically does this for you, or at least implement ICloneable which is a standard practice for how to do this. (MemberwiseClone() only does a shallow copy which is why you might need to implement ICloneable). Then you could even write a test that looks through your assembly for everything that implements ICloneable and tests it.

Upvotes: 2

Paul Alexander
Paul Alexander

Reputation: 32377

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.

Upvotes: 3

Related Questions