出名的有 jMock, EasyMock. 但用過後, jMock 固然看得頭昏也看不明白搞什麼, EasyMock 用起來是簡單一點, 但對於 expected data 的檢查也是十分麻煩, 尤其是我只想檢查 expected input 的某些 attributes, EasyMock 做起來十分痛苦.
後來找到一個叫 SevenMock 的 mock framework, 用起來十分簡單, expected input 的 validation 的可讀性更是非常好.
下面是一個使用例子. 假設我有一個 TradeService的 實作要測:
public class TradeServiceImpl {
  private tradeDao tradeDao;
  private fundService fundService;
  public void setTradeDao(TradeDao tradeDao) {
      this.tradeDao = tradeDao;
  }
  public void setFundService(FundService fundService) {
      this.fundService = fundService;
  }
  public boolean inputTrade(Trade trade) {
      if (this.fundService.isEnoughBalance(trade.getPrice().mulplity(trade.getQty()))) {
          this.fundService.deductBalance(trade.getPrice().mulplity(trade.getQty()));
          trade.setStatus(Trade.Status.NEW);
          this.tradeDao.createTrade(trade);
          return true;
      )
      return false;
  }
}
下面便是利用 SevenMock 的測試
public class TradeServiceImplTest {
   @Test
   public void testFoo() {
        // create test target and mocks
        TradeService tradeService = new TradeServiceImpl();
        MockController mockControl = new MockController();
        TradeDao tradeDao = (TradeDao)mockControl.getMock(TradeDao.class);
        FundService fundService= (FundService)mockControl.getMock(FundService.class);
      
        // Inject Mock objects to class-to-be-tested
        tradeService.setTradeDao(tradeDao);
        tradeService.setFundService(fundService);
        // Set Expected Results
        mockControl.expect(new FundServiceMockImpl() {
            public boolean isEnoughBalance(BigDecimal amount){
                // check if incoming value is correct
                assertEquals(BigDecimal.valueOf(1000*400), amount);
               
                // preset behavior: return true
                return true;
            }
        });
        mockControl.expect(new FundServiceMockImpl() {
            public void deductBalance(BigDecimal amount){
                // check if incoming value is correct
                assertEquals(BigDecimal.valueOf(1000*400), amount);
            }
        });
        mockControl.expect(new TradeDaoMockImpl() {
            public void createTrade(Trade trade) {
                assertEquals(Trade.Status.NEW,trade.getStatus());
                return result;
            }
        });
        // Finish setting expected behavior, then run test now
        Trade myTrade = new Trade();
       myTrade.setPrice(BigDecimal.valueOf(1000));
       myTrade.setQty(BigDecimal.valueOf(400));
       myTrade.setStatus(Trade.Status.NONE);
     
       boolean result = tradeService.inputTrade(myTrade);
        // verify results       
        assertTrue(result);
        assertEquals(Trade.Status.NEW, myTrade.getStatus());
       
        // Mock control will verify if there is missing step in invocation
        mockControl.verify();
    }
}
// dummy implementation class of mocked interfaces for use of SevenMock
class TradeDaoMockImpl implements TradeDao {
    // all empty methods....
}
class FundServiceMockImpl implements FundService {
    // all empty methods....
}
用起來十分直觀容易明白. 其中一個缺點是要 mock 的 interface, 需要一個 implementation class 來 override 為 anonymous class. 我這裡是弄一個 empty 的 implementation (TradeDaoMockImpl 及 FundServiceMockImpl), 靠 IDE 要弄這麼一個 empty implementation 很容易. 但要是你本身就有 implementations, 只要constructor 沒有什麼特別的 initialization, 直接用你自己的 implementation 來用也無不可, 反正不會真的 invoke 到它裡面的 method.
誠意推介.
