Skip to content

Instantly share code, notes, and snippets.

@JayBazuzi
Created May 27, 2014 04:32
Show Gist options
  • Save JayBazuzi/ffb7daa9450b2a647302 to your computer and use it in GitHub Desktop.
Save JayBazuzi/ffb7daa9450b2a647302 to your computer and use it in GitHub Desktop.
Asserts that this type correctly implements "value" equality semantics.
static class ValueTypeAssertions
{
class C
{
}
/// <summary>
/// Asserts that this type correctly implements "value" equality semantics.
/// </summary>
/// <param name="a">An example of T</param>
/// <param name="aPrime">Another T which should be the "same" as a</param>
/// <param name="b">Another T which should be different from a</param>
public static void HasValueEquality<T>(T a, T aPrime, T b)
{
Assert.IsTrue(a.Equals(aPrime));
Assert.IsTrue(a.GetHashCode().Equals(aPrime.GetHashCode()));
Assert.IsFalse(a.Equals(b));
Assert.IsFalse(a.GetHashCode().Equals(b.GetHashCode()));
Assert.IsFalse(a.Equals(null));
Assert.IsFalse(a.Equals(new C()));
Assert.AreEqual(a, aPrime);
Assert.AreNotEqual(a, b);
}
}
@dennisdoomen
Copy link

So what's so special about this example that Fluent Assertions' a.Should().Be(aPrime) doesn't do? The GetHashCode()?

@JayBazuzi
Copy link
Author

I often want to create small types with "value" equality semantics, as opposed to reference equality. C# makes me write a bunch of boilerplate code each time, but that doesn't mean I should have to write the same boilerplate tests. Instead, I want a single simple test that says "Assert that my type has value equality".

@dennisdoomen
Copy link

Then Should().Be() should work. It uses Object.Equals under the hood.

@JayBazuzi
Copy link
Author

There's nothing wrong with Should().Be(), but I want the whole assert you see above.

a.ShouldHaveValueEquality(anotherA, b); // asserts those 7 things

@dennisdoomen
Copy link

So it should assert that Equals returns true and GetHashCode returns the same value for both objects?

@JayBazuzi
Copy link
Author

It should assert the 7 things listed in my Gist.

@bgeihsgt
Copy link

bgeihsgt commented Jun 7, 2014

Just forked and made the boilerplate to create a, aPrime, b less cumbersome. Next steps I think would be to create rich error messaging by either integrating with FluentAssertions or creating good messages with these asserts. I also think we may want to add an Assert to make sure it inherits from IEquatable.

@dennisdoomen
Copy link

I see that, but FA usually takes two objects and executes an assertion on that, but your gist uses 4 objects. Value Equality in .NET means that two objects return true for Equals, return the same value for GetHashCode even though ReferenceEquals returns false. I could have an extension method that takes a factory lamba like @bgeihsgt just showed.

@JayBazuzi
Copy link
Author

@bgeihsgt and @dennisdoomen: Here's another iteration. I split out equality from inequality, so you can deal with case-insensitive stuff:

new Foo("bar") should equal new Foo("BAR")

https://gist.github.com/JayBazuzi/74e5fc153c44b550e956

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment