Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 770
Buthrakaur Avatar asked Nov 25 '09 15:11

Buthrakaur


2 Answers

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.

like image 150
Peter Avatar answered Oct 22 '22 10:10

Peter


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.

like image 33
Mark Seemann Avatar answered Oct 22 '22 11:10

Mark Seemann