views:

415

answers:

3

Hi! I'm new to unit-testing and NUit in particular. I'm just typing some examples from the book which refers to Java and JUnit. But I'm using C# instead.

The problem is: I've got a class with overriden methods such as Equals() and GetHashCode(), but when I am trying to compare two objects of this class with Assert.AreEqual() my code is not called, so I get an exception.

Assert.True(MyClass.Equals(MyClass2)) does work well. But I don't wanna use this construction instead of Assert.AreEqual(). Where the problem can be?

Here is the class:

public class Money
{
    public int amount;
    protected string currency;

    public Money(int amount, string currency)
    {
        this.amount = amount;
        this.currency = currency;
    }

    public new bool Equals(object obj)
    {
        if (obj == null)
            return false;

        Money money = (Money)obj;
        return (amount == money.amount) 
                && (Currency().Equals(money.Currency()));
    }

    public new int GetHashCode()
    {
        return (string.Format("{0}{1}", amount, currency)).GetHashCode();
    }

    public static Money Dollar(int amount)
    {
        return new Money(amount, "USD");
    }
    public static Money Franc(int amount)
    {
        return new Money(amount, "CHF");
    }

    public Money Times(int multiplier)
    {
        return new Money(amount * multiplier, currency);
    }

    public string Currency()
    {
        return currency;
    }
}

And the test method itself:

[TestFixture]
public class DollarTest
{
    [Test]
    public void TestMultiplication()
    {
        Money five = Money.Dollar(5);
        Assert.True(Money.Dollar(10).Equals(five.Times(2)));  // ok
        Assert.AreEqual(Money.Dollar(10), five.Times(2));     // fails
    }
}

Thanks.

+17  A: 

The problem is you're hiding Equals, not overriding it. Well done - your unit test has found a bug :)

Your code should be:

public override bool Equals(object obj)
{
    Money money = obj as Money;
    if (money == null)
        return false;

    return (amount == money.amount && currency == money.currency);
}

(This will prevent it from throwing an exception if you give it the wrong type, too.)

I've made the string equality test simpler too - operator overloading can be very helpful :)

By the way, you almost certainly want to:

  • Change Currency to be a property, not a method
  • Add an Amount property
  • Probably change the type of amount to be decimal instead of int
  • Make the fields private and readonly
  • Seal the class
  • Add operator overloads for == and !=
  • Possibly add a * operator overload to do the same as Times
  • Avoid string formatting when calculating the hash (there are dozens of answers showing better hash implementations)

EDIT: I've just reread that you're using an example from a book. Does the book really hide instead of overriding the Equals method? I suggest you get a new book, if so (unless it's being a deliberate example of when it's wrong to use hiding!)... which book is it?

Jon Skeet
Hopefully this bug proves the value of unit testing!
RichardOD
This is awesome. Great start to the day.
Ty
yay, I was right :)
Samuel Carrijo
Yes, that's a decision! Thanks a lot.I read "Test Driven Development: By Example" by Kent Beck. Examples are written in Java, so the method definition is:public bool equals(Object object);But I was writing C# code, so IDE-helper asked me if I wanted to write "public new bool Equals(object o)" instead of just "public bool Equals(object o)". So, this is my mistake, not to mention all the solutions of the problem.Although thanks for the tips. I'll consider them for future experience ;)
stasal
Ah, right - that's all right then. If it had been a C# book I'd have been dismayed :)
Jon Skeet
A: 

I suspect your problem is that you haven't overridden overload the equality == operator. Under the hood the Assert.AreEqual is probably using ==.

See Operator Overloading Tutorial.

Update: I ran the NUnit test through the debugger and it does indeed use the Equals method and not the == operator.

sipwiz
You can't override operators - you can only *overload* them. NUnit almost certainly *won't* be using overloaded operators - it's unnecessarily complicated when it can just call Equals...
Jon Skeet
A: 

I found it confusing that implementing the IEquatable interface, which also has an

Equals(T other)

method, posed me with the same problem as described above.

The only reason I chose to use the IEquaytable interface above overriding the Equals method was not to have to do the type check.

In the end I had to use the following code

public bool Equals(CustomTag other)
{
   return (other.Name.Trim().ToLower() == Name.Trim().ToLower());
}

public override bool Equals(object o)
{
    if (o is CustomTag)
    {
        return Equals(o as CustomTag);
    }
    return false;
}

but then I though, why not just leave the IEquatable interface for what it is and only override the Equals method. (less code = better)

sjors miltenburg