5

I am writing Unit Tests and thinking about the scenario of a type that implements IClonable. So of course I want to have a Unit Test that tests the Clone() method.

[Test]
public void CloneObject()
{
    MyType original = new MyType();
    MyType clone = (MyType)original.Clone();

    // Assert for equality
}

So my first task it to have an Assert for the equality. I see following options:

  • going through all properties (fields) of MyType and check them one by one
  • override Equals() in MyType to let MyType say if two instances are equal (consider that sometimes equality for tests is considered different of equality for production code)
  • Check with some kind of serialization if the instances are equal (for that scenario MyType would have to be [Serializable], but that sometimes is hard to do if it has e.g. Interface Properties)
  • ...??

For the first two I can setup my tests and they work well. But what if I change MyType and add an additional property? If Clone() doesn't copy this and I don't add it in the list of checked properties or the equals method my test still passes even if the property does not get copied.

How do you solve this kind of tests?

joerg
  • 717
  • 2
  • 8
  • 18
  • Have you considered Json (de)serialization or does that suffer from the same limitations as binary serialization (`[Serializable]`) in your view? – Alex Booker Aug 11 '15 at 09:56

2 Answers2

7

You can use the FluentAssertions library (a TDD must-have, IMO), which has a ShouldBeEquivalent method that performs graph comparison of the two objects:

original.ShouldBeEquivalentTo(clone);

The comparison algorithm can be customized via the optional options parameter; see their wiki for detailed info.

To make the test future-proof (i.e., it breaks when new properties are added to the class, but not to the Clone method), you might want to test cloning an object with all its properties set to random non-default values. AutoFixture can do that for you.

// Arrange
var fixture = new Fixture();
var original = fixture.Create<MyType>();

// Act
var clone = original.Clone();

// Assert
clone.ShouldBeEquivalentTo(original);

Useful links:

dcastro
  • 66,540
  • 21
  • 145
  • 155
  • @joerg no prob! Be sure to check out what else you can do with FluentAssertions - it's full of goodies! – dcastro Aug 11 '15 at 10:16
  • Oops... one step back... I just added an `int` to my type... I would have to set a non-default-value in my test to fail... just adding this property and the test still passes... – joerg Aug 11 '15 at 10:19
  • Thanks again :) Test PASSED... maybe I should start a new question with "What Frameworks do you need for UnitTests"... seems that you need quite a few – joerg Aug 11 '15 at 10:33
  • @joerg That question would be considered [off-topic](http://stackoverflow.com/help/on-topic) though :P Discussing that on a [SO chat room](https://chat.stackoverflow.com/rooms/7/c) might be a good idea though! I learned about those two very useful tools a while ago from [this answer](http://stackoverflow.com/a/24922904/857807). Another useful tool is a decent mocking framework, in case you're not aware of those. I personally prefer Moq. – dcastro Aug 11 '15 at 10:39
1

I would favor implementing Equals(). I see no reason why Equals() needs to produce different results in production vs. in tests.

If you don't do that, your Assert could call object.ReferenceEquals(), as follows:

Assert.IsTrue(object.ReferenceEquals(expectedObject, objectInHand));
Jeff Prince
  • 658
  • 6
  • 13
  • I found reading [this](http://blog.ploeh.dk/2012/06/22/Test-specificEqualityversusDomainEquality/) very helpful... the ReferenceEquals should Assert to false because the clone should have a different reference than the original – joerg Aug 12 '15 at 06:17