Ayende @ Wiki

In Part I we looked at setting up an expectation on our mock object. We told Rhino Mocks that we expected the "GetMinutesUntilNextUpdate" to be called, and that we expected it to return zero. In this part we will look at some slight variations on that test. We also introduced the metaphor of Rhino Mocks being a concerned parent (which as we will see was an unfortunate choice)

First, lets add the following method to our SimpleRSSSystem object

      public string LastUpdateMessage()
      {
          return "Last updated on " + _Aggregator.LastUpdate().ToString("MM/dd/yyyy");
      }


And let's modify our test to call this method.

        [Test]
        public void What_Happens_When_We_Call_A_Method_That_We_Didnt_Expect()
        {
            MockRepository mockery = new MockRepository();
            IRSSAggregator rssGetter = mockery.CreateMock<IRSSAggregator>();

using (mockery.Record()) { Expect.Call(rssGetter.MinutesUntilNextUpdate()).Return(0); }

using (mockery.Playback()) { SimpleRSSSystem system = new SimpleRSSSystem(rssGetter); Debug.WriteLine(system.DisplayCountdownMessage()); Debug.WriteLine(system.LastUpdateMessage()); //* } }


This test will fail, with a message along the lines of "...Expected #0, Actual #1.". This is what I call a "Daughter On A Date" error. Rhino Mocks is telling us to slow down - we have permission to call MinutesUntilLastUpdate, but no one mentioned anything about touching LastUpdateMessage.

This is easy enough to fix.

        [Test]
        public void What_Happens_When_We_Call_A_Method_That_We_Didnt_Expect_Fixed()
        {
            MockRepository mockery = new MockRepository();
            IRSSAggregator rssGetter = mockery.CreateMock<IRSSAggregator>();

using (mockery.Record()) { Expect.Call(rssGetter.MinutesUntilNextUpdate()).Return(0); Expect.Call(rssGetter.LastUpdate()).Return(new DateTime(2007, 12, 10)); }

using (mockery.Playback()) { SimpleRSSSystem system = new SimpleRSSSystem(rssGetter); Debug.WriteLine(system.DisplayCountdownMessage()); Debug.WriteLine(system.LastUpdateMessage()); }

}


Now our expectations are set correctly, and the test passes.

The user of our system calls and says - "hey this last update message is worthless, I need something a lot more specific". We respond by changing SimpleRssSystem.LastUpdateMessage to the following:

        public string LastUpdateMessage()
        {
            return "Last updated on " + _Aggregator.LastUpdate().ToString("MM/dd/yyyy") + " at " + _Aggregator.LastUpdate().ToString("hh:mm");
        }


We go back and run our test. And it fails. The message we are getting is something like "...Expected #1, Actual #2.". I think of this as the "Captain Happyhands" error. Rhino Mocks is telling us that we had permission to call the method one time but not to get carried away and use it as often as we wanted.

Fortunately, Rhino's Repeat clause allows us to work around this (here we want to repeat twice, but there are a number of self explanatory options).

        [Test]
        public void What_Happens_When_We_Call_A_Method_That_More_Times_Than_We_Expected()
        {
            MockRepository mockery = new MockRepository();
            IRSSAggregator rssGetter = mockery.CreateMock<IRSSAggregator>();

using (mockery.Record()) { Expect.Call(rssGetter.MinutesUntilNextUpdate()).Return(0); Expect.Call(rssGetter.LastUpdate()).Repeat.Twice().Return(new DateTime(2007, 12, 10)); }

using (mockery.Playback()) { SimpleRSSSystem system = new SimpleRSSSystem(rssGetter); Debug.WriteLine(system.DisplayCountdownMessage()); Debug.WriteLine(system.LastUpdateMessage()); }

}


We can also use Rhino's SetupResult class to achieve a similar result. This tells Rhino Mocks to return the specified value if the method is called, but to not worry about it beyond that.



[Test] public voidWhat_Happens_When_We_Call_A_Method_That_More_Times_Than_We_ExpectedRelaxed() { MockRepository mockery = new MockRepository(); IRSSAggregator rssGetter = mockery.CreateMock<IRSSAggregator>();

SetupResult.For(rssGetter.LastUpdate()).Return(new DateTime(2007, 12, 10));

using (mockery.Record()) { Expect.Call(rssGetter.MinutesUntilNextUpdate()).Return(0); }

using (mockery.Playback()) { SimpleRSSSystem system = new SimpleRSSSystem(rssGetter); Debug.WriteLine(system.DisplayCountdownMessage()); Debug.WriteLine(system.LastUpdateMessage()); }

}



The difference is subtle but important. Sticking with the dating theme, and without getting crude, the Repeat.X is a trusted boyfriend whereas the SetupResult is a visit to the doctors. In the first case Rhino cares and will enforce the rules, but in the second Rhino is prepared to relax a little.

These rules apply under the strict semantics of regular Mocks. Part III will illustrate that Dynamic mocks are quite a bit more forgiving.

ScrewTurn Wiki version 2.0 Beta. Some of the icons created by FamFamFam.