Tim Barcz

Sponsors

The Lounge

Wicked Cool Jobs

Groups and Affiliations

Syndication

News

RhinoMocks Exploration : RhinoMocks “Caching” Values?

RhinoExplorationOccasionally on the RhinoMocks mailing list a question pops up about various pieces of the framework that people struggle with. This post is the first in hopefully an ongoing series where I address specific underpinnings of RhinoMocks to you, the reader.  I’ll do my best to explain the inner workings of the code and what is going on under the covers while trying to keep it “real”.  If you have specific things you want to see, let me know and I will do my best to put together the tutorial.

That said, let’s get started…

Occasionally a question will come in about RhinoMocks caching a value and that something must be a bug in the framework.  Consider the following very simple test where we want to return the current date/time from a property getter.  You have a component with a gettable time but no setter.  In order to fake this out we use the Stub() method return a "known" value.

   1: [TestFixture]
   2: public class TimTests
   3: {
   4:     [Test]
   5:     public void DateTimeDive()
   6:     {
   7:         var stub = MockRepository.GenerateStub<IFoo>();
   8:  
   9:         stub.Stub(x => x.Current).Return(DateTime.Now);
  10:         
  11:         Thread.Sleep(5000);
  12:  
  13:         Console.WriteLine(stub.Current);
  14:     }
  15: }
  16:  
  17: public interface IFoo
  18: {
  19:     DateTime Current { get; }
  20: }

What time do you suppose is written out the console given the Thread.Sleep cal? The common thinking is that the Stub() method stores a method to be called to return the value at the time of execution when in reality what is happening is that the stub.Stub(x => x.Current).Return(DateTime.Now) line is evaluated at runtime at the time the Stub is set.  Therefore, in the above code, the Console.WriteLine(stub.Current) will actually write the time from 5 seconds (5000 milliseconds prior) when the stub call was made. In other words, when Stub() is called, the resulting value is calculated and ready to be returned, it's not deferred for execution.

What is going on is that deep in the bowels of RhinoMocks there are things called Expectations, MockStates, and Recorders.  In the code above, when Stub() is called, what happens is a two step process (but appears as one, given the fluency, or dot-dot notation, of the call).

    1. An expectation is created with a null return value (stub.Stub(x=>x.Current))
    2. The expectation is updated with the known return value (.Return(DateTime.Now))
    (Note that if you try to write code without the second piece, you will get an InvalidOperationException when you try to use the stubbed property stating:
        failed: Method 'IFoo.get_Current();' requires a return value or an exception to throw.
        System.InvalidOperationException: Method 'IFoo.get_Current();' requires a return value or an exception to throw.
        C:\dev\OSS\rhino-tools\mocks\Rhino.Mocks\Expectations\AbstractExpectation.cs(342,0): at Rhino.Mocks.Expectations.AbstractExpectation.ReturnOrThrow(IInvocation invocation, Object[] args)
        C:\dev\OSS\rhino-tools\mocks\Rhino.Mocks\Impl\StubReplayMockState.cs(63,0): at Rhino.Mocks.Impl.StubReplayMockState.DoMethodCall(IInvocation invocation, MethodInfo method, Object[] args)
        C:\dev\OSS\rhino-tools\mocks\Rhino.Mocks\Impl\ReplayMockState.cs(118,0): at Rhino.Mocks.Impl.ReplayMockState.MethodCall(IInvocation invocation, MethodInfo method, Object[] args)
        C:\dev\OSS\rhino-tools\mocks\Rhino.Mocks\MockRepository.cs(678,0): at Rhino.Mocks.MockRepository.MethodCall(IInvocation invocation, Object proxy, MethodInfo method, Object[] args)
        C:\dev\OSS\rhino-tools\mocks\Rhino.Mocks\Impl\RhinoInterceptor.cs(96,0): at Rhino.Mocks.Impl.RhinoInterceptor.Intercept(IInvocation invocation)
        c:\OSS\Castle\Tools\Castle.DynamicProxy2\Castle.DynamicProxy\AbstractInvocation.cs(202,0): at Castle.DynamicProxy.AbstractInvocation.Proceed()
        at IFooProxybc98a6ed8f1b4aa3b9ea36d89f80e6af.get_Current()
        C:\dev\OSS\rhino-tools\mocks\Rhino.Mocks.Tests\TimTests.cs(43,0): at Rhino.Mocks.Tests.TimTests.err()
    There is an "expectation" created when the call is made and that expectation is added to a recorder (There are ordered and unordered method recorders, by default (the most common scenario) you will be using unordered recorders). These recorders record the mocked object (our stub object here), the method being called, and the "expectation". The call to .Return(DateTime.Now) updates the expectation with the desired return value.  In the RhinoMocks framework this code is very simple but it's important to note this value is executed at the point this code is run.

   1: public IMethodOptions<T> Return(T objToReturn)
   2: {
   3:     expectation.ReturnValue = objToReturn;
   4:     return this;
   5: }

You can see from the code above that setting up a return value simply updated the expectation's return value.
I hope you've followed the above code.  The RhinoMocks codebase is not simple per se and you do not need to understand it in depth to use it but I think in this case it help (as I see it often pop up on the mailing list).

So what do you do if you want something to be called in a delayed fashion?  In our example I want the call to IFoo.Current to represent the DateTime.Now when I call it (as opposed to when I set it up). To do that, simply use the "Do()" method.  Here is an example that provides the same functionality as above, only the return value of the Stub is deferred until the stub is actually called.

   1: [Test]
   2: public void Realtime_results_with_Do()
   3: {
   4:     var stub = MockRepository.GenerateStub<IFoo>();
   5:  
   6:     stub.Stub(x => x.Current).Do(new Func<DateTime>(() => DateTime.Now));
   7:  
   8:     Thread.Sleep(5000);
   9:  
  10:     Console.WriteLine(stub.Current);
  11: }

I hope this helps a bit with the confusion around RhinoMocks and perceived "caching" of values.  If you have questions about this or other areas of RhinoMocks, I would love to tear into them for you, just ask!


Posted 08-21-2009 5:26 PM by Tim Barcz
Filed under:

[Advertisement]

Comments

DotNetShoutout wrote RhinoMocks Exploration : RhinoMocks “Caching” Values? - Tim Barcz - Devlicio.us
on 08-21-2009 9:12 PM

Thank you for submitting this cool story - Trackback from DotNetShoutout

Gil Zilberfeld wrote re: RhinoMocks Exploration : RhinoMocks “Caching” Values?
on 08-22-2009 2:26 AM

Hi Tim,

Thanks for the post.

It never occured to me that people think that the value is a value and not a function. Did you see many cases where this happens? Why do you think it's confusing?

Thanks,

Gil

Tim Barcz wrote re: RhinoMocks Exploration : RhinoMocks “Caching” Values?
on 08-24-2009 1:32 PM

@Gil,

I think what is confusing is when something get's executed.  It may not be that many people have issues or questions around this area, but every now and again a question pops up.  I thought there may be others out there with similar questions who never ask them.

Tim

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco <-- NEW Friend!
NServiceBus <-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)