为什么不调用我的Equals方法?

时间:2014-01-14 02:14:40

标签: c# tdd mspec

我正在通过Kent Beck的TDD作为学术练习,但是使用MSpec来编写测试。在下面的工作示例中,我想介绍一个扭曲,以便我不能简单地将文本复制出来,我发现这种方式我倾向于遇到我必须解决的问题,因此,最终学习更多。我相信这是其中一种情况。

我通过Kent的'钱'示例的一部分。这是我的类结构: enter image description here

我有以下两个测试环境:

[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;
}

Times()方法返回一个包含结果的Money类型的新对象,即对象是不可变的。上面的第一个上下文传递,表明Equals正在按预期工作,即它忽略了对象类型,只要它们都是从Money继承的,并且只比较金额和货币字段是相等的。第二个上下文失败,输出类似于:

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

平等定义为金额(价值)和货币相同;应该忽略对象的实际类型,因此预期的结果是,如果我正在测试与Money或Franc对象的相等性,只要金额和货币字段相同,则无关紧要。但是,事情没有按计划进行。调试时,我的Equals()方法甚至没有被调用。显然我在这里并不理解。我确信当我知道它时,解决方案将会非常明显,但我看不出它。任何人都可以就我需要做些什么来提出建议吗?

这是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);
}

2 个答案:

答案 0 :(得分:2)

完全相同的实现将如下所示。看看它是否有帮助。

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);
}

答案 1 :(得分:0)

正如@Harrison评论的那样,问题是Times类的Franc方法的结果类型。它未通过测试规范,因为它返回Money对象,但规范需要Franc实例。更改规范以要求Money对象或覆盖Times方法以返回Franc实例。

<强>更新

更新测试规范后,您更改了行:

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));

但是,在属性上,主题的类型仍然是:

[Subject(typeof(Franc), "multiplication")]

所以我认为它仍然期待Franc实例而不是Money实例。