views:

222

answers:

2

Hi!

I've a problem setting up a test for an Equals method on an object.

The object in question is defined by this interface:

public interface IHours {
    ITimeOfDay OpenAt { get; set; }
    ITimeOfDay CloseAt { get; set; }
    DateTime ValidFrom { get; set; }
    DateTime ValidTo { get; set; }
    bool isCovered(DateTime time);
}

and it contains references to ITimeOfDay defined such:

public interface ITimeOfDay {
    DateTime Time { get; set; }
    int Hour { get; }
    int Minute { get; }
    int Second { get; }
}

Now I want the Equals of the Hours : IHours to check the OpenAt and CloseAt IHours. To set this up I try to stub those property-values out, and just return true and false depending on what my particular test needs them to be.

    [SetUp]
    public virtual void SetUp() {
        mocks = new MockRepository();

        defaultHours = getHours();
        otherHours = getHours();

    }

    [TearDown]
    public virtual void TearDown() {
        mocks.ReplayAll();
        mocks.VerifyAll();
    }

    [Test(Description = "Equals on two Hours should regard the fields")]
    public void Equals_TwoValueEqualledObjects_Equal() {
        var openAt = mocks.Stub<ITimeOfDay>();
        var closeAt = mocks.Stub<ITimeOfDay>();

        closeAt                                   //this is line 66, referenced in the error stacktrace
            .Stub(o => o.Equals(null))
            .IgnoreArguments()
            .Return(true);

        openAt
            .Stub(c => c.Equals(null))
            .IgnoreArguments()
            .Return(true);
        mocks.ReplayAll();

        defaultHours.OpenAt = openAt;
        otherHours.OpenAt = openAt;

        defaultHours.CloseAt = closeAt;
        defaultHours.CloseAt = closeAt;

        Assert.That(defaultHours, Is.EqualTo(otherHours));
        Assert.That(defaultHours.GetHashCode(), Is.EqualTo(otherHours.GetHashCode()));
    }

But I get this cryptic error when I run it:

System.InvalidOperationException: Type 'System.Boolean' doesn't match the return type   'System.Collections.Generic.IList`1[NOIS.Model.Interfaces.IAircraft]' for method 'IAircraftType.get_Aircrafts();'
at Rhino.Mocks.Expectations.AbstractExpectation.AssertTypesMatches(Object value)
at Rhino.Mocks.Expectations.AbstractExpectation.set_ReturnValue(Object value)
at Rhino.Mocks.Impl.MethodOptions`1.Return(T objToReturn)
at Nois.Test.Model.Entities.HoursTest.Equals_TwoValueEqualledObjects_Equal() in HoursTest.cs: line 66

The IAircraftType interface is a part of the same namespace, but nowhere in the test, interface or implementing class is it referenced. I do not understand why it interferes. There is no reference to it as far as I can gather.

I am using - Rhino.Mocks v3.5.0.1337 - NUnit.Framework v2.5.0.8332

Anyone have any idea?

+3  A: 

Yeah this is complicated - the error is crazy, this has nothing to do with IAircraft. Essentially the issue is that an interface is not a class and hence does not inherit from object. In other words - closeAt does not have an Equals method to stub out.

As a matter of fact, its probably a bit of a language flub that you can even call Equals() explicitly on an object cast to an interface.

Two ways to fix this then

  1. Don't mock the interface, mock the implementation mocks.Stub() - this does have an equals method that is virtual so your code will work.
  2. Even better, add an Equals method to your interface. Once you do that you will be able to override it and since all classes inherit from object you won't have to implement it explcitly ever (unless you want to).

In other words

var intrface = MockRepository.GenerateStub<IInterface>();
intrface.Stub(x => x.Equals(null)).IgnoreArguments().Return(true);

Breaks when

public interface IInterface {
}

but works when

public interface IInterface {
  bool Equals(object obj);
}
George Mauer
Aaaaaahhhh!Oooooohh!Of course! (forehead smack!)
Tomas
I tweeted this to Ayende, hopefully he can weigh in on the carzy error
George Mauer
A: 

Unless I'm missing something, Time of Day should be a simple immutable object. So I'd just implement it as a small, tested value class. Then you can use the real Equals method.

Steve Freeman
This is true in this particular case. I did do this for a while, but my blasted curiosity wouldn't let me leave it at that. For which I am now glad, as George's answer above made a terrible Friday many times better.
Tomas