October 25, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Testing with Mock Objects

  • October 23, 2006
  • By Daniel Gartner
  • Send Email »
  • More Articles »

Mock objects are useful when performing unit testing and can provide a best solution in terms of testability. I will present the new NMock 2.0 (Release Candidate 1) library and show how you can use it to implement mock objects.

Mock objects are used as abstractions for dependencies of a class under test and are used to effectively isolate the class under test to ensure proper test verification. For example, if Class A uses a class that is an abstraction for a network service, I don't want to have to go through the effort of setting up a connection and verifying connectivity just to be able to test. Therefore, I create a mock for the other class, and define how it should behave. I then write my tests for Class A based on the expected behavior of the dependency. This effectively decouples a class from its dependencies and makes the entire development and testing process much more smooth.

A mock object is a testing pattern that is used to test interfaces rather than specific implementations of classes. This is done by defining the expectations of the methods of those classes, and then verifying that your expectations were met after calling the methods. This can be useful for classes that are not yet developed, difficult to setup for testing, or dependent on an external resource such as a database or web service, or for classes that perform prohibitively slowly, such that the execution time for the unit tests using the actual implementation would be too great.

The Example Application

In the spirit of the Halloween season, I have set about to develop an application for a pumpkin patch. The pricing of pumpkins (or any commodity) can fluctuate greatly based on current geopolitical conditions. The business stakeholder has asked that the system retrieves the actual price of a pound of pumpkin as determined by current market conditions in real-time. I have decided to implement this using a web service that queries the global pumpkin syndicate database and returns the current price for a pound of pumpkins. The pumpkin prices at the patch are also dictated by the number of days until Halloween (the closer to Halloween, the more expensive a pumpkin will become). The stakeholder would like to be able to set a markup percent for the price of pumpkins on a daily basis. This will be a value that is stored in the database.

As it turns out, this application is an excellent candidate for the use of mock objects. I have two external dependencies, the database which stores the daily markup amount, and the web service that queries for the real-time price of a pound of pumpkins. For the web service, I will create an interface called IPumpkinPriceService with a single method that returns the current price of pumpkins. For database interaction, I will create an interface called IPumpkinDataManager that gets & sets the today's price markup percent, as well as some other basic methods.

namespacePumpkinPatch.Services
{
   public interface IPumpkinPriceService
   {
      double GetPricePerPound();
   }
}

namespace PumpkinPatch.Data
   public interface IPumpkinDataManager
   {
      IList<Pumpkin> GetPumpkins();
      int AddPumpkin(Pumpkin p);
      doubleTodaysMarkupPercent
      {
        get;
        set;
      }
   }
}

In the business logic layer of the application, I will create a PumpkinPricer class that uses an instance of IPumpkinDataManager and IPumpkinPricer service to calculate the price of a pumpkin based on its weight, today's markup and the market price of a pound of pumpkin,

namespace PumpkinPatch.BusinessLogic
{
   public class PumpkinPricer : IPumpkinPricer
   {
      private IPumpkinPriceService pumpkinPriceService;
      private IPumpkinDataManager pumpkinDataManager;
      public PumpkinPricer(IPumpkinDataManager pumpkinDataMgrIn, IPumpkinPriceService pumpkinPriceServIn)
     {
         this.pumpkinDataManager = pumpkinDataMgrIn;
         this.pumpkinPriceService = pumpkinPriceServIn;
      }

      #region IPumpkinPricer Members  

      public double GetPrice(Pumpkin p)
     {
         doublepricePerPound = this.pumpkinPriceService.GetPricePerPound();
         double todaysMarkupPercent = this.pumpkinDataManager.TodaysMarkupPercent;
         return (pricePerPound * p.Weight) * (1.0 + todaysMarkupPercent);
      }
      #endregion
   }
}

Note that I have used constructor injection to provide added flexibility and make it easier to test the PumpkinPricer class. To learn more about dependency injection, please see my last article here.

Now, to verify that the PumpkinPricer calculates the price of a pumpkin correctly, I will create unit tests that pass mock objects to the constructor of the PumpkinPricer object. This ensures that my unit tests will isolate the PumpkinPricer, and won't be dependant on calls to the database or calls to a remote web service. The goal of my tests is to prove that, if the IPumpkinDataManager and the IPumpkinPriceService work as expected then the PumpkinPricer will correctly calculate the price.

Creating the Tests with Mock Objects

In my unit tests, I am going to use mock objects to represent the IPumpkinDataManager and IPumpkinPriceService objects in order to isolate the PumpkinPricer from its external dependencies. With NMock 2.0, you first create an instance of your mock object before defining its expected behavior. Since the price of pound of pumpkins changes by the second, I want to simplify my test by simply returning an unchanging amount each time the GetPricePerPound() method is called.

Mockery mocks = new Mockery();
IPumpkinPriceService mockService = mocks.NewMock<IPumpkinPriceService>();
//Define the expectations of the mockService 
//(the method named GetPricePerPound will return 1.49 as the price)
Expect.Once
.On(mockService)
.Method("GetPricePerPound")
.WithNoArguments()
.Will(Return.Value(1.49));

So, each time the GetPricePerPound() method is called on the mockService, 1.49 will be returned. I also want to set the mockDataManager's TodaysMarkupPercent property to return 0.10. Properties can be set using GetProperty.

IPumpkinDataManager mockDataManager = mocks.NewMock<IPumpkinDataManager>();
//Define the expectations of the mockDataManager  
//todays markup is 10% over the normal price   
Expect.Once
.On(mockDataManager)
.GetProperty("TodaysMarkupPercent")
.Will(Return.Value(0.10));

My expectation is that the TodaysMarkupPercent property will be invoked once and will return the value 0.10. Now that I have defined how the mocks will behave, it's time to test the PumpkinPricer.

//instantiate a 5 lb pumpkin
Pumpkin p = new Pumpkin();
p.Weight = 5.00;

//instantiate the PumpkinPricer using the mocks
PumpkinPricer pricer = new PumpkinPricer(mockDataManager, 
mockService);
//get the calculated price
double actualPrice = pricer.CalculatePrice(p);
//get the expected price
double expectedPrice = (5.00  * 1.49) * 1.10;
//compare actual to expected 
Verify.That(actualPrice, Is.EqualTo(expectedPrice));
mocks.VerifyAllExpectationsHaveBeenMet();

To test the CalculatePrice() method of the PumpkinPricer, I passed it the two mocks via its constructor. The mocks were used by the CalculatePrice() method to calculate the price of the 5 pound pumpkin. Since I setup the TodaysMarkupPercent property of the IPumpkinDataManager mock to return 0.10 for this test, and the GetPricePerPound() method of the mockService should return 1.49, then the expected return value of the CalculatePrice method should be equivalent to 5 (# of lbs of the pumpkin) * 1.49 (price / lb) * (1 + 0.10 (today's markup)). I used NMock's Verify class to verify that the actualPrice is equal to the expectedPrice. If this were not the case, then a NMock.Internal.ExpectationException would be thrown. Lastly, we call the VerifyAllExpectationsHaveBeenMet() method. This ensures that the GetPricePerPound method and the TodaysMarkupPercent property were actually invoked as expected during the course of the test.

Evolving the Design

Now, since requirements change frequently, I want to modify the IPumpkinPriceService to retrieve historical pumpkin prices. Therefore, I am going to overload the GetPricePerPound method to take a DateTime variable as a parameter. The change to the interface is shown below. Also, since this is making a call to an external 3rd-party web service not under my authority, it would be realistic to pass some sort of credentials to the 3rd party. Therefore, I have added properties to the interface to retrieve the required credentials that must be passed to the GetPricePerPound() method.

public interface IPumpkinPriceService
{
   double GetPricePerPound(string userName, string password);
   double GetPricePerPound(DateTime dt, string userName, string password);
   string PriceServiceUserName { get; }
   string PriceServicePassword { get; }
}

Now, I update the CalculatePrice() method of the PumpkinPricer class to reflect the change to IPumpkinPriceService.

public double CalculatePrice(Pumpkin p)
{
   string userName =   this.pumpkinPriceService.PriceServiceUserName;
   string password = this.pumpkinPriceService.PriceServicePassword;
   double pricePerPound = 
this.pumpkinPriceService.GetPricePerPound(DateTime.Today,
userName, password); double todaysMarkupPercent = this.pumpkinDataManager.TodaysMarkupPercent; return (pricePerPound * p.Weight) * (1.0 + todaysMarkupPercent); }

I am also going to have to modify the expectations of the mock IPumpkinPriceService to take the appropriate parameters for the GetPricePerPound() method and to verify that the PriceServiceUserName and PriceServicePassword properties are accessed during the execution of the CalculatePrice() method. Here are the updated expectations of the CalculatePriceTest method.

//initialize a username and password for this 
test
string testUserName = "TestUserName";
string testPassword = "TestPassword";

//the PriceServiceUserName should be accessed at least once
Expect.AtLeastOnce
.On(mockService)
.GetProperty("PriceServiceUserName")
.Will(Return.Value(testUserName));

//the PriceServicePassword should be accessed at least once
Expect.AtLeastOnce
.On(mockService)
.GetProperty("PriceServicePassword")
.Will(Return.Value(testPassword));

//Define the expectations of the mockService
//(the method named GetPricePerPound will return 1.49 as the price)
Expect.Once
.On(mockService)
.Method("GetPricePerPound")
.With(DateTime.Today, testUserName, testPassword)
.Will(Return.Value(1.49));

Note that I passed parameters to the GetPricePerPound method using the With statement.

Conclusion

In the above example mock objects were created to act as stand-ins for actual implementations in order to isolate the unit under test (in this case, the PumpkinPricer class). This makes it easier to detect defects during development. You can find out more information about NMock by viewing this tutorial and this cheatsheet.

Downloads

Download the sample code: MockObjectsCode.zip




Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel