Rhino Mocks: Re-assign a new result for a method o

2019-05-25 04:30发布

问题:

I know I can do this:

IDateTimeFactory dtf = MockRepository.GenerateStub<IDateTimeFactory>();
dtf.Now = new DateTime();
DoStuff(dtf); // dtf.Now can be called arbitrary number of times, will always return the same value
dtf.Now = new DateTime()+new TimeSpan(0,1,0);  // 1 minute later
DoStuff(dtf); //ditto from above

What if instead of IDateTimeFactory.Now being a property it is a method IDateTimeFactory.GetNow(), how do I do the same thing?

As per Judah's suggestion below I have rewritten my SetDateTime helper method as follows:

    private void SetDateTime(DateTime dt) {
        Expect.Call(_now_factory.GetNow()).Repeat.Any();
        LastCall.Do((Func<DateTime>)delegate() { return dt; });
    }

but it still throws "The result for ICurrentDateTimeFactory.GetNow(); has already been setup." errors.

Plus its still not going to work with a stub....

回答1:

George,

Using your updated code, I got this to work:

MockRepository mocks = new MockRepository();

[Test]
public void Test()
{
    IDateTimeFactory dtf = mocks.DynamicMock<IDateTimeFactory>();

    DateTime desiredNowTime = DateTime.Now;
    using (mocks.Record())
    {
        SetupResult.For(dtf.GetNow()).Do((Func<DateTime>)delegate { return desiredNowTime; });
    }
    using (mocks.Playback())
    {
        DoStuff(dtf); // Prints the current time    
        desiredNowTime += TimeSpan.FromMinutes(1);  // 1 minute later    
        DoStuff(dtf); // Prints the time 1 minute from now
    }
}

void DoStuff(IDateTimeFactory factory)
{
    DateTime time = factory.GetNow();
    Console.WriteLine(time);
}

FWIW, I don't believe you can accomplish this using stubs; you need to use a mock instead.



回答2:

I know this is an old question, but thought I'd post an update for more recent Rhino Mocks versions.

Based on the previous answers which use Do(), there is a slightly cleaner (IMO) way available if you are using AAA in Rhino Mocks (available from version 3.5+).

    [Test]
    public void TestDoStuff()
    {
        var now = DateTime.Now;
        var dtf = MockRepository.GenerateStub<IDateTimeFactory>();
        dtf
           .Stub(x => x.GetNow())
           .Return(default(DateTime)) //need to set a dummy return value
           .WhenCalled(x => x.ReturnValue = now); //close over the now variable

        DoStuff(dtf); // dtf.Now can be called arbitrary number of times, will always return the same value
        now = now + new TimeSpan(0, 1, 0); // 1 minute later
        DoStuff(dtf); //ditto from above
    }

    private void DoStuff(IDateTimeFactory dtf)
    {
        Console.WriteLine(dtf.GetNow());
    }


回答3:

You can use Expect.Call to accomplish this. Here's an example using the record/playback model:

using (mocks.Record())
{
   Expect.Call(s.GetSomething()).Return("ABC"); // 1st call will return ABC
   Expect.Call(s.GetSomething()).Return("XYZ"); // 2nd call will return XYZ
}
using (mocks.Playback())
{
   DoStuff(s);
   DoStuff(s);
}


回答4:

Ok, so my first answer doesn't work for you because GetSomething may be called multiple times, and you don't know how many times.

You're getting into some complex scenario here -- unknown number of method invocations, yet with different results after DoSomething is called -- I recommend breaking up your unit test to be simpler, or you'll have to have unit tests for your unit tests. :-)

Failing that, here's how you can accomplish what you're trying to do:

bool shouldReturnABC = true;
using (mocks.Record())
{
   Expect.Call(s.GetSomething()).Repeat.Any();

   LastCall.Do((Func<string>)delegate()
   {
      return shouldReturnABC ? "ABC" : "XYZ";
   }
}
using (mocks.Playback())
{
   DoStuff(s);
   shouldReturnABC = false;
   DoStuff(s);
}