George Mauer
George Mauer

Reputation: 122062

Rhino Mocks: Re-assign a new result for a method on a stub

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....

Upvotes: 3

Views: 3372

Answers (4)

David Tchepak
David Tchepak

Reputation: 10464

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

Upvotes: 7

Judah Gabriel Himango
Judah Gabriel Himango

Reputation: 60011

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.

Upvotes: 1

Judah Gabriel Himango
Judah Gabriel Himango

Reputation: 60011

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

Upvotes: 0

Judah Gabriel Himango
Judah Gabriel Himango

Reputation: 60011

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

Upvotes: 0

Related Questions