views:

114

answers:

1

I have an interface with a CopyFrom() method that copies all properties from another object. I have a test that performs several VerifyGet() calls to ensure that each property was retrieved from the passed object, e.g.:

Thing target = new Thing();
IThing source = new Mock<IThing>();

target.CopyFrom(source.Object);
source.VerifyGet(t => t.Foo);
source.VerifyGet(t => t.Bar);

I'd like a way to iterate over the properties of IThing though and verify that each was copied automatically so the test will fail if someone adds a property but forgets to copy it. Is there a way to do this via Moq? I tried;

foreach (var prop in typeof(IThing).GetProperties())
{
    source.VerifyGet(t => prop.Invoke(t, null));
}

but it didn't work since the lambda did not represent a property accessor. I think there should be a way to create something via the Expression class, but I am not familiar enough with LINQ to figure out what should be in there.

+2  A: 

I don't think such a thing is possible with Moq, but first of all you need to question whether your test is relevant. What do you really wish to test here?

Is it important that the CopyFrom method reads any properties? It could definitely read all the properties without writing to them to the new instance, so such an Interaction-based test doesn't really prove anything.

I'm guessing that what you would really like to test is that the properties of the target are equal to the properties of the source?

Assuming that the properties on IThing are writable, you could create a Stub with all the properties set using the SetupAllProperties method:

var sourceStub = new Mock<IThing>();
sourceStub.SetupAllProperties();
sourceStub.Object.Bar = "Bar";
sourceStub.Object.Foo = "Foo";

You would then need to compare the target to the source to see if all properties match. You could do that by implementing a Test-Specific Equals method in a class that wraps the real target.

If you think that is too much work, you may want to check out AutoFixture's Likeness class that gives you a general-purpose Test-Specific equality comparison. That would allow you to continue the test like this:

var expectedResult = new Likeness<IThing>(sourceStub.Object);

target.CopyFrom(sourceStub.Object);

Assert.AreEqual(expectedResult, target);

Likeness uses Reflection to simply loop over all the public properties in the wrapped object and see if the compared object has the same values for those properties.

Mark Seemann
You are correct that VerifyGet() doesn't necessarily mean everything was copied, but it was close enough for my case. The trouble with just running through and verifying all values are the same is that if someone adds a property and forgets to copy it, then in the test, both properties will simply be the default value and the test will still pass.
Eddie Deyo
@OAB: That's right, unless you use a Reflection-based equality evaluator like Likeness (pun intended).
Mark Seemann
How does Likeness avoid the problem of the property having the default? If I add a new string property Baz to IThing and forget to add it to CopyFrom, won't Likeness find that Baz on both sourceStub and target are null?
Eddie Deyo
@OAB: Likeness does not address that problem, but AutoFixture's Fixture.CreateAnonymous does, because by default it assigns non-default values to all writable properties :)
Mark Seemann