July 23, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Getting Test Doubles in Place

  • January 3, 2008
  • By Jeff Langr
  • Send Email »
  • More Articles »

I'm building a portfolio manager, something that will track my stock purchases. My current need is that it calculate the total value of its contained holdings. A holding is a stock symbol and the number of associated shares. The portfolio value is the sum of the current price of each symbol times the corresponding number of shares. An external service, perhaps supplied by NASDAQ, provides the ability to look up a stock's value.

Were I to build Portfolio without considering the need to unit test it, the implementation might look like Listing 1.

Listing 1: Portfolio.

import java.util.*;

public class Portfolio {
   private Map<String,Integer> holdings =
      new HashMap<String,Integer>();

   public int value() {
      int total = 0;
      for (Map.Entry<String,Integer>
          entry: holdings.entrySet()) {
         String symbol = entry.getKey();
         int shares = entry.getValue();
         total += new NASDAQLookupService().currentValue(symbol)
               * shares;
      }
      return total;
   }

   public void purchase(String symbol, int shares) {
      holdings.put(symbol, shares);
   }
}

I code using test-driven development, so my primary interest is in ensuring that there's an easy way to drive and verify each line of code in the value method. The problem is that NASDAQ keeps returning a different value each time I send it a request for a symbol's price. The solution is to provide what's known as a "test double" for the NASDAQLookupService object. This test double will emulate the NASDAQLookupService behavior, but fix it for purposes of testing. (Other similar terms for test doubles are fakes, stubs, and mocks.)

When using test doubles, I have two primary concerns: first, what does the test double look like, and second, how do I incorporate it into the production code? With regard to the first concern, the test double is a simple implementation of the same interface used by the real object. For my Portfolio class, I create a fake StockLookupService that implements an interface named StockLookupService. NASDAQLookupService, too, implements this interface.

Now that I have a fake lookup service, how do I get it into the the class that I'm testing (the target class)? The usual solution is to pass the fake into the target via a constructor or setter. This technique is most common, and demonstrated by most introductory articles on mocking. I'll present this rudimentary injection technique, and then focus on a couple additional ways to inject the test double.

Constructor/Setter Injection

In Listing 2, I present an altered Portfolio class. The constructor provides the ability for a client to pass in a reference to a StockLookupService. This reference can of course point to either a NASDAQLookupService or to a fake lookup service.

Listing 2: An injectable Portfolio.

import java.util.*;

public class Portfolio {
   private StockLookupService service;
   private Map<String,Integer> holdings =
      new HashMap<String,Integer>();

   public Portfolio(StockLookupService service) {
      this.service = service;
   }

   public int value() {
      int total = 0;
      for (Map.Entry<String,Integer>
         entry: holdings.entrySet()) {
         String symbol = entry.getKey();
         int shares = entry.getValue();
         total += service.currentValue(symbol) * shares;
      }
      return total;
   }

   public void purchase(String symbol, int shares) {
      holdings.put(symbol, shares);
   }
}

A bit of relevant test code for Portfolio appears in Listing 3. The @Before initialize method instantiates an anonymous fake impementation of the StockLookupService. It then uses this to instantiate an instance of the target class Portfolio.

Listing 3: PortfolioTest.

import static org.junit.Assert.*;
import org.junit.*;

public class PortfolioTest {
   private static final int CURRENT_MSFT_VALUE = 100;
   private static final String MSFT = "MSFT";
   private Portfolio portfolio;

   @Before
   public void initialize() {
      StockLookupService service = new StockLookupService() {
         public int currentValue(String symbol) {
            if (MSFT.equals(symbol))
               return CURRENT_MSFT_VALUE;
            return 0;
         }
      };

      portfolio = new Portfolio(service);
   }

   @Test
   public void multiplesValueByShares() {
      portfolio.purchase(MSFT, 10);
      assertEquals(CURRENT_MSFT_VALUE * 10, portfolio.value());
   }
}




Page 1 of 3



Comment and Contribute

 


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

 

 


Sitemap | Contact Us

Rocket Fuel