问题
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
}
}
回答1:
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 bedecimal
instead ofint
- 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?
回答2:
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)
回答3:
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.
回答4:
You can write framework agnostic asserts using a library called Should. It also has a very nice fluent syntax which can be used if you like fluent interfaces. I had a blog post related to the same.
http://nileshgule.blogspot.com/2010/11/use-should-assertion-library-to-write.html
来源:https://stackoverflow.com/questions/1373358/nunit-doesnt-work-well-with-assert-areequal