开发者

Stub one method of class and let other real methods use this stubbed one

I have a TimeMachine class which provides me current date/time values. The class looks like this:

public class TimeMachine
{
    public virtual DateTime GetCurrentDateTime(){ return DateTime.Now; };
    public virtual DateTime GetCurrentDate(){ return GetCurrentDateTime().Date; };
    public virtual TimeSpan GetCurrentTime(){ return GetCurrentDateTime().TimeOfDay; };
}

I'd like to use TimeMachine stub in my tests in such way that I'd just stub the GetCurrentDateTime method and let the other 2 methods use the 开发者_高级运维stubbed GetCurrentDateTime method so as I don't have to stub all the three methods. I tried to do write the test like this:

var time = MockRepository.GenerateStub<TimeMachine>();
time.Stub(x => x.GetCurrentDateTime())
    .Return(new DateTime(2009, 11, 25, 12, 0, 0));
Assert.AreEqual(new DateTime(2009, 11, 25), time.GetCurrentDate());

But the test fails. GetCurrentDate returns default(DateTime) instead of using GetCurrentDateTime stub internally.

Is there any approach I could use to achieve such behavior or is it just some basic conceptual feature of RhinoMocks I don't catch at the moment? I know I could just get a rid of those two GetDate/Time methods and inline the .Date/.TimeOfDay usage, but I'd like to understand whether this is possible at all.


If the method is marked as virtual, a Stub will not call the original method, even if you didn't Stub the method. You can force RhinoMocks to call the original method by doing:

var time = MockRepository.GenerateStub<TimeMachine>();
time.Stub(x => x.GetCurrentDateTime()).Return(new DateTime(2009, 11, 25, 12, 0, 0));

time.Stub(x => x.GetCurrentDate()).CallOriginalMethod(OriginalCallOptions.NoExpectation);

Assert.AreEqual(new DateTime(2009, 11, 25), time.GetCurrentDate());

It's the third (seperated) line that makes RhinoMocks call the underlying, original method.


Change your TimeMachine to an abstract class:

public abstract class TimeMachine
{
    public abstract DateTime GetCurrentDateTime();
    public DateTime GetCurrentDate(){ return GetCurrentDateTime().Date; };
    public TimeSpan GetCurrentTime(){ return GetCurrentDateTime().TimeOfDay; };
}

For production purposes, you can create a concrete implementation of TimeMachine like this:

public class SystemTimeMachine : TimeMachine
{
    public override DateTime GetCurrentDateTime()
    {
        return DateTime.Now;
    }
}

All classes consuming TimeMachine can now be injected with the abstraction, but in production you can wire up your object graph with SystemTimeMachine.


I just found out that it's possible to achieve this by not using virtual on those two methods - it protects the methods from being overriden when generating stub.

public class TimeMachine
{
    public virtual DateTime GetCurrentDateTime(){ return DateTime.Now; };
    public DateTime GetCurrentDate(){ return GetCurrentDateTime().Date; };
    public TimeSpan GetCurrentTime(){ return GetCurrentDateTime().TimeOfDay; };
}

The test passes now.


A stub simply provides canned answers to method and property calls, it knows nothing about the actual implementation of TimeMachine. I'm afraid you will have to set up results for each of the 3 methods (or for the particular method you would like to test).


I'm not sure which version of Rhino.Mocks you're using, but what I would do is make only the GetCurrentDateTime() method virtual (like an earlier poster suggested), then create your mock object using PartialMock(). There are lots of ways to set things up, but the following should work:

var mocks = new MockRepository();
var time = mocks.PartialMock<TimeMachine>();
using (mocks.Record())
{
   Expect.Call(time.GetCurrentDateTime()).Return(new DateTime(2009, 11, 25, 12, 0, 0));
}
using (mocks.Playback())
{
   Assert.AreEqual(new DateTime(2009, 11, 25), time.GetCurrentDate());
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜