English 中文(简体)
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.

最佳回答

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.

问题回答

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.

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




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签