Mock object is basically a mirage of the real object as they posses the same qualities by don't do anything. Mock objects really shines when working with some entity that continuously changes like time, temperature, air speed etc. In this post I will introduce mock objects and how can they be useful when unit testing such entities.
I took the idea from a great article published on AspAlliance. The article "Beginning to Mock Using Rhino Mocks and MbUnit" is written by Ben Hall. The idea is that we are testing that your method returns the correct image. The correctness of the image depends on the time of the day. So, if it is day time then "sun.jpg" is returned and if it is night time then "moon.jpg" is returned. Let's first see the implementation without using the Mock objects.
[Test]
public void should_return_sun_image_when_it_is_day_time()
{
string imageName = ImageOfTheDayService.GetImage(DateTime.Now);
Assert.AreEqual(imageName, "sun.jpg");
}
The above method is used to test for the image during the day time. Let's check out the implementation of the ImageOfTheDayService.GetImage method.
public static string GetImage(DateTime dt)
{
int hour = dt.Hour;
if (hour > 6 && hour < 21) return "sun.jpg";
return "moon.jpg";
}
The problem is that you can only test the day time result during the day and the night time result during night. This is inconvenient. You can hack the implementation of your tests and force the day time and night time to happen. Check out the implementation below:
[Test]
public void should_return_sun_image_when_it_is_day_time()
{
DateTime dt = DateTime.Parse("02/02/02 5:00 AM");
int hour = dt.Hour;
int lessHour = 0;
if (hour < 6)
lessHour = 15 - hour;
if (hour > 21)
lessHour = 15 - hour;
string imageName = ImageOfTheDayService.GetImage(dt.AddHours(lessHour));
Assert.AreEqual(imageName, "sun.jpg");
}
[Test]
public void should_return_night_image_when_it_is_night_time()
{
int hour = DateTime.Now.Hour;
int lessHour = 0;
if (hour < 21)
{
lessHour = 21 - hour;
}
string imageName = ImageOfTheDayService.GetImage(DateTime.Now.AddHours(lessHour));
Assert.AreEqual(imageName, "moon.jpg");
}
In the should_return_sun_image_when_it_is_day_time test I am using 5:00 AM as the start time. The problem is that 5:00 AM is still night (according to the demo application). In order to a successful test I need to provide the day time. So, I do some funky stuff and crank the time to 3:00 PM which is the time during the day. Now, I test my GetImage method using the new fake time.
The funky hack makes the test pass but leaves behind ugliness in the test methods. Now, let's introduce the Mock objects to clear out this mess. I am using Moq framework to create my mock objects but you can use any framework you like. First, I need to create an interface for the DateTime which I can feed to the Moq framework to return me a mocked object.
public interface IDateTime
{
int GetHour();
}
Now, the GetImage method of the ImageOfTheDayService will take the IDateTime as the parameter instead of the DateTime.
public static string GetImage(IDateTime mock)
{
int hour = mock.GetHour();
if (hour > 6 && hour < 21) return "sun.jpg";
return "moon.jpg";
}
Now, let's check out the test using mock objects.
[Test]
public void MOCKING_should_return_sun_image_when_it_is_day_time()
{
var mock = new Mock<IDateTime>();
mock.Expect(e => e.GetHour()).Returns(15); // 3:00 PM
Assert.AreEqual("sun.jpg", ImageOfTheDayService.GetImage(mock.Object));
}
[Test]
public void MOCKING_should_return_night_image_when_it_is_night_time()
{
var mock = new Mock<IDateTime>();
mock.Expect(e => e.GetHour()).Returns(21); // 9:00 PM
Assert.AreEqual("moon.jpg", ImageOfTheDayService.GetImage(mock.Object));
}
The test becomes much simpler now. All we are saying is that we expect a call on the "GetHour()" method of the IDateTime interface. The method will return "15" (day test). We are not concerned about what is the current time of the day since we are putting expectations and results. Finally, we pass the IDateTime to the GetImage method and compares the output with our expected results.