Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't my Equals method get called?

Tags:

c#

tdd

mspec

I'm working through Kent Beck's TDD by Example as an academic exercise, but using MSpec to write the tests. When following worked examples, I like to introduce a twist so that I can't simply copy the text out rote, I find that way I tend to run into problems that I have to solve and as a result, end up learning much more. I believe this is one of those occasions.

I'm part way through Kent's 'money' example. Here's the class structure I have: enter image description here

I have the following two test contexts:

[Subject(typeof(Money), "Equality")]
public class when_comparing_different_classes_for_equality
{
    Because of = () => FiveFrancs = new Franc(5, "CHF");
    It should_equal_money_with_currency_set_to_francs = () => FiveFrancs.Equals(new Money(5, "CHF")).ShouldBeTrue();
    static Franc FiveFrancs;
}

[Subject(typeof(Franc), "multiplication")]
public class when_multiplying_a_franc_amount_by_an_integer
{
    Because of = () => FiveFrancs = new Franc(5, null);
    It should_be_ten_francs_when_multiplied_by_2 = () => FiveFrancs.Times(2).ShouldEqual(Money.Franc(10));
    It should_be_fifteen_francs_when_multiplied_by_3 = () => FiveFrancs.Times(3).ShouldEqual(Money.Franc(15));
    static Franc FiveFrancs;
}

The Times() method returns a new object of type Money containing the result, i.e. the objects are immutable. the first context above passes, suggesting that Equals is working as expected, i.e. it ignores the the object types, so long as they are both inherited from Money, and only compares that the amount and currency fields are equal. The second context fails with output similar to this:

Machine.Specifications.SpecificationException
  Expected: TDDByExample.Money.Specifications.Franc:[15]
  But was:  TDDByExample.Money.Specifications.Money:[15]
   at TDDByExample.Money.Specifications.when_multiplying_a_franc_amount_by_an_integer.<.ctor>b__2() in MoneySpecs.cs: line 29

Equality is defined as the amount (value) and currency being the same; the actual type of the object is supposed to be ignored, so the intended result is that it shouldn't matter if I'm testing equality with Money or Franc objects, as long as the amount and currency fields are the same. However, things are not working as planned. When debugging, my Equals() methods are not even getting called. There is clearly something I'm not understanding here. I am sure the solution will be blindingly obvious when I know it, but I can't see it for looking. Can anyone offer a suggestion as to what I need to do to make this work?

Here's the implementation of Equals():

public bool Equals(Money other)
{
    return amount == other.amount && currency == other.currency;
}

public override bool Equals(object obj)
{
    if (ReferenceEquals(null, obj))
        return false;
    if (ReferenceEquals(this, obj))
        return true;
    return Equals(obj as Money);
}
like image 592
Tim Long Avatar asked Jan 14 '14 02:01

Tim Long


People also ask

How do you call equals method in Java?

In Java terms, they are equal, which is checked with equals : String some = "some string"; String other = "some string"; boolean equal = some. equals(other); Here, equals is true .

When equals method is called in HashMap?

equals() method: This method is used to check whether 2 objects are equal or not. This method is provided by the Object class. You can override this in your class to provide your implementation. HashMap uses equals() to compare the key to whether they are equal or not.

How does equals () method work What does it do?

The equals() method compares two strings, and returns true if the strings are equal, and false if not. Tip: Use the compareTo() method to compare two strings lexicographically.

What is the difference between the == operator and equals () method?

We can use == operators for reference comparison (address comparison) and . equals() method for content comparison. In simple words, == checks if both objects point to the same memory location whereas . equals() evaluates to the comparison of values in the objects.


1 Answers

A fully-complete implementation of equality would look like this. See if it helps.

protected bool Equals(Money other)
{
    // maybe you want this extra param to Equals?
    // StringComparison.InvariantCulture
    return amount == other.amount 
      && string.Equals(currency, other.currency);
}

public override bool Equals(object obj)
{
    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    var other = obj as Money;
    return other != null && Equals(other);
}

public override int GetHashCode()
{
    unchecked
    {
        return (amount * 997) ^ currency.GetHashCode();
    }
}

public static bool operator ==(Money left, Money right)
{
    return Equals(left, right);
}

public static bool operator !=(Money left, Money right)
{
    return !Equals(left, right);
}
like image 128
spender Avatar answered Sep 29 '22 13:09

spender