Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NUnit doesn't work well with Assert.AreEqual

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 want to use this construction instead of Assert.AreEqual(). Where can the problem 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
    }
}
like image 992
stasal Avatar asked Sep 03 '09 13:09

stasal


2 Answers

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?

like image 136
Jon Skeet Avatar answered Oct 01 '22 12:10

Jon Skeet


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 thought, why not just leave the IEquatable interface for what it is and only override the Equals method. (less code = better)

like image 22
Sjors Miltenburg Avatar answered Oct 01 '22 14:10

Sjors Miltenburg