Beginning to Mock with Rhino Mocks and MbUnit - Part 1
page 4 of 7
by Ben Hall
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 43833/ 75

A Mocking Example

To start with, let us solve the dilemma set out at the beginning - testing against the system time. A method, GetImageForTimeOfDay(), should return a filename to a image based on the time of day it currently is; for example, sun.jpg for daytime and a moon.jpg for night time.

This is how the tests might look if we were not using mock objects. I have cut down a lot of the code which would actually be required, for example taking into account timezones and time of year.

Listing 1

[Test] 
public void DaytimeTest() 
{ 
   int currentHour = DateTime.Now.Hour; 
  
   if (currentHour > 6 && currentHour < 21) 
   { 
      string expectedImagePath = "sun.jpg"; 
      ImageManagement image = new ImageManagement(); 
      string path = image.GetImageForTimeOfDay(); 
      Assert.AreEqual(expectedImagePath, path); 
   } 
   else 
   { 
      Assert.Ignore("Can only be tested during the day"); 
   } 
} 
  
[Test] 
public void NighttimeTest() 
{ 
   int currentHour = DateTime.Now.Hour; 
  
   if (currentHour < 6 || currentHour > 21) 
   { 
      string expectedImagePath = "moon.jpg"; 
      ImageManagement image = new ImageManagement(); 
      string path = image.GetImageForTimeOfDay(); 
      Assert.AreEqual(expectedImagePath, path); 
   } 
   else 
   { 
      Assert.Ignore("Can only be tested at night"); 
   } 
} 

The method could be implemented like this:

Listing 2

public string GetImageForTimeOfDay() 
{ 
   int currentHour = DateTime.Now.Hour; 
  
   if (currentHour > 6 && currentHour < 21) 
      return "sun.jpg"; 
   else 
      return "moon.jpg"; 
} 

This is far from ideal as we can only ever run one test at a time as they can only be run at certain points in the day.  If we change the code, we can only test one part of the actual requirement, unless we modify the system clock. Having difficult to execute tests is just one reason why we might stop running the tests altogether. How can we trust the application even works?

By using a mock object and abstracting away from DateTime.Now.Hour we will be able to have much more reliable tests.  In order to be able to use mock objects, we need to a use an interface design for obtaining the time. Rhino Mocks then uses this interface to create the mock implementation of the object which allows us to call the methods on the interface just like we would with a real implementation.

We then use parameter injection to define which implementation should be used within the method. 

When using mock objects, the tests would look like this.

Listing 3

[Test] 
public void DaytimeTest() 
{ 
   MockRepository mocks = new MockRepository(); 
   IDateTime timeController = mocks.CreateMock<IDateTime>(); 
  
   using (mocks.Record()) 
   { 
      Expect.Call(timeController.GetHour()).Return(15); 
   } 
  
   using (mocks.Playback()) 
   { 
      string expectedImagePath = "sun.jpg"; 
      ImageManagement image = new ImageManagement(); 
      string path = image.GetImageForTimeOfDay(timeController); 
      Assert.AreEqual(expectedImagePath, path); 
   } 
} 
  
[Test] 
public void NighttimeTest() 
{ 
   MockRepository mocks = new MockRepository(); 
   IDateTime timeController = mocks.CreateMock<IDateTime>(); 
   using (mocks.Record()) 
   { 
      Expect.Call(timeController.GetHour()).Return(1); 
   } 
  
   using (mocks.Playback()) 
   { 
      string expectedImagePath = "moon.jpg"; 
      ImageManagement image = new ImageManagement(); 
      string path = image.GetImageForTimeOfDay(timeController); 
      Assert.AreEqual(expectedImagePath, path); 
   } 
} 

With the implementation for the system now being:

Listing 4

public interface IDateTime 
{ 
   int GetHour(); 
} 

 

public class ImageManagement 
{ 
   public string GetImageForTimeOfDay(IDateTime time) 
   { 
      int currentHour = time.GetHour(); 
  
      if (currentHour > 6 && currentHour < 21) 
      { 
         return "sun.jpg"; 
      } 
      else 
      { 
         return "moon.jpg"; 
      } 
   }
}  

Now we have a set of useful tests which can be executed no matter what time it is, however, the mock syntax may look very different to you.

In the first line we are defining a MockRepository which is the main interaction with the Rhino Mocks framework.

Listing 5

MockRepository mocks = new MockRepository(); 

We then define our mock object. There are two methods for creating the mock, one is CreateMock<> while the other is DynamicMock<>. According to the documentation, CreateMock has strict semantics, meaning if a call is made on the mock object which it was not expecting then it will throw an exception.  DynamicMock has dynamic semantics where unexpected calls are accepted with a null/zero value being returned and no exception is thrown.

Listing 6

IDateTime timeController = mocks.CreateMock<IDateTime>(); 

Next, we enter recording mode for the mock object. This is an important stage where we define what we want the mock object to do. We define the calls we expect to be made on the object and the values which should be returned. This is how we can create a known state for our system as everything is defined in recording mode. In this example we are saying that we expect that the GetHour() method will be called on our mock implementation (timeController) and that this should always return the value 15 to the calling code. 

Listing 7

using (mocks.Record()) 
{ 
   Expect.Call(timeController.GetHour()).Return(15); 
} 

From a Test Driven Development (TDD) approach, this encourages us to focus on the design a lot more upfront as we define all of the method calls and the return values.

After we have defined all of our expectations we enter Playback mode.  In this mode we write the unit test as normal.

Listing 8

using (mocks.Playback()) 
{ 
   string expectedImagePath = "sun.jpg"; 
   ImageManagement image = new ImageManagement(); 
   string path = image.GetImageForTimeOfDay(timeController); 
   Assert.AreEqual(expectedImagePath, path); 
} 

Notice how the GetImageForTimeOfDay() method now takes in the IDateTime object as a parameter. This approach allows us a lot more flexibility and allows us to define which implementation, either the mock object or real, at runtime it should use instead of the method deciding for us.  If we wanted to clean up the code for production, we could overload the method with no parameters, which simply initializes the default implementation and passes it in as an argument.

Finally, now we have written the test, we can write the code to make them pass.

Listing 9

public string GetImageForTimeOfDay(IDateTime time) 
{ 
   int currentHour = time.GetHour(); 
  
   if (currentHour > 6 && currentHour < 21) 
   { 
      return "sun.jpg"; 
   } 
   else 
   { 
      return "moon.jpg"; 
   } 
} 
  
public class DateTimeController : IDateTime 
{ 
   public int GetHour() 
   { 
      return DateTime.Now.Hour; 
   } 

We are still calling DateTime.Now.Hour, however our GetImageForTimeOfDay() is no longer dependant on this.  

An application could then look like this:

Listing 10

static void Main(string[] args) 
{ 
   ImageManagement image = new ImageManagement(); 
   string path = image.GetImageForTimeOfDay(new DateTimeController()); 
   Console.WriteLine(path); 
   Console.ReadLine(); 
} 

Hopefully from this, you can see that using Rhino Mocks greatly increases the capabilities of the tests which we are writing, allowing us to replace the hard to test components. Also, mock objects have improved the design of the system by encouraging us to program against interfaces, one of the core OO design rules, and abstracting away from the core framework. If we have to change the way we handle obtaining the time, maybe from a external time server, we only have to change the internal method in our DateTimeController.


View Entire Article

User Comments

Title: "Program against interfaces"   
Name: Rogério Liesenfeld
Date: 2009-10-06 5:01:32 PM
Comment:
What exactly is meant here by "program against interfaces, one of the core OO design rules"?

Creating an IDateTime interface with a single DateTimeController implementation class is absolutely NOT what was intended by the GoF book (Design Patterns) when they said "program to interface, not an implementation".

That "core OO design rule" is about something else: polymorphism; and it's really only applicable in the context of reusable OO software, specifically for dependencies between subsystems. Not at all the case for the example in this article.
Title: Question   
Name: bangi
Date: 2009-05-06 9:37:40 AM
Comment:
Is it possible to mock an object to is called by the method that I am calling. Eg. 1SomeInterface has a method called GetUsers. GetUsers calls a method called findusers which belongs to a Interface IDataAccess. Is it correct to make a mock object for IDataAccess in my Test class hoping that when I call ISomeInterface when ISomeInterface gets to the IDataAccess.findusers() the mock version will be user instead of the real method.
Title: Thanks   
Name: dotnetguts
Date: 2008-10-27 2:23:56 PM
Comment:
Thanks, Good Information.

DotNetGuts
http://dotnetguts.blogspot.com
Title: Excellent   
Name: Iran
Date: 2008-09-13 7:17:49 AM
Comment:
Thanks a lot Ben for providing a simple start up guide for Rhino mocks
Title: Stubs v Mocks   
Name: Ben Hall
Date: 2008-05-27 6:49:28 AM
Comment:
\
Title: I recomended this book   
Name: Carlos
Date: 2008-05-27 6:04:34 AM
Comment:
This post is very well, but are confused concepts(STUBS-MOCKS)
I recommend reading this book is really good.

http://www.manning.com/osherove/
Title: Diference Stubs and Mocks   
Name: Carlos
Date: 2008-05-27 4:28:18 AM
Comment:
I think these confusing terms between STUBS and MOCKS.
You've created a mock, for help to test the class, this is a stub not a mock. I recommend you read this article.

http://martinfowler.com/articles/mocksArentStubs.html
Title: Great Tutorial   
Name: Steve Smith
Date: 2008-03-22 12:55:29 PM
Comment:
It didn't click for me until I read this - been trying to learn Rhino Mocks off and on for the last week. Nice job!
Title: Thanks for sharing   
Name: Jeena
Date: 2008-01-25 9:32:33 AM
Comment:
Very good article. Thanks a lot
Title: Good introduction to Rhino Mocks   
Name: Tod Birdsall
Date: 2007-12-27 5:55:43 PM
Comment:
Thanks for the nice introduction. It was very clear and easy to follow.
Title: .   
Name: Pawel Tarnik
Date: 2007-10-10 8:39:38 AM
Comment:
Nice article, I look forward to reading the next part.
Thanks.
Title: Beginning to Mock with Rhino Mocks and MbUnit - Part 1   
Name: Lee
Date: 2007-10-09 7:46:47 PM
Comment:
Thanks for the article - it's clear and easy to follow.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-03-29 11:50:36 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search