mockito(とpowermock)でstaticメソッドの試験

そんなこんなでかれこれ1年半位後回しにしちゃっていた、クラスメソッドのテストについて。
powermockを利用してテストする事とします。
http://code.google.com/p/powermock/


以前のサンプル
Mockitoの実験(モックを用いた単体テスト) - mokkouyou2001の日記


まぁ、題材は大体おんなじですというか流用です。


新しい部分としては、
計算結果に通貨記号を付与して返すメソッド追加。
(通貨記号はなぜかDaoのクラスメソッドとして実装されます)

package logic;

import dao.SettingsDao;

public class CalcLogic {
 /**
  * 設定の税率で金額を求め、送料を付与して返す。
  *
  * @param price
  *            金額
  * @return 合計金額
  */
 public int calcPrice(int price) {
   // インラインでインスタンスを取得しているのでMoc化できない。
   // SettingsDao settingsDao = new SettingsDao();
   // メソッド化

   SettingsDao settingsDao = getInstance();
   return (int) Math.floor(price * (1 + settingsDao.getTaxRate()))
       + settingsDao.getTransportation();
 }

 /**
  * 設定の税率で金額を求め、送料を含んだ料金に関して、文字列表現を返す(通貨記号を付与する)。
  *
  * @param price
  *            金額
  * @return 合計金額(通貨記号付きの文字列表現)
  */
 public String calcDispPrice(int price) {
   return calcPrice(price) + SettingsDao.getCurrencySign();
 }

 /**
  * 設定情報取得Daoを返却する。
  *
  * @return
  */
 protected SettingsDao getInstance() {
   return new SettingsDao();
 }
}


通貨記号を返すクラスメソッド追加
ここにあるのは何で?というのは気にしない。きっと一度だけ起動時に初期化みたいなことがされるんじゃないかな。ということで。

package dao;

public class SettingsDao {
 /**
  * DBより税率を取得して返す。
  *
  * @return 税率
  */
 public double getTaxRate() {
   return 0.5;
 }

 /**
  * DBより送料を取得して返す。
  *
  * @return 送料
  */
 public int getTransportation() {
   return 500;
 }

 /**
  * 通貨記号を返す。
  * @return 通貨記号
  */
 public static String getCurrencySign() {
   return "円";
 }
}

そんでもって、テストはこんな感じ
以前のメソッドのテストも残っています。

package logic;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import dao.SettingsDao;
//PowerMock利用
@RunWith(PowerMockRunner.class)
//SettingsDaoに対して利用
@PrepareForTest(SettingsDao.class)
public class CalcLogicMockItTest {
 @Test
 public void testTestCalcPrice() {
   CalcLogic logic = spy(new CalcLogic());
   SettingsDao dao = spy(new SettingsDao());

   when(dao.getTaxRate()).thenReturn(0.03);
   when(dao.getTransportation()).thenReturn(200);

   when(logic.getInstance()).thenReturn(dao);
   int calcPrice = logic.calcPrice(200);
   assertEquals(406, calcPrice);
 }
 @Test
 public void testTestCalcPriceStatic() {
   CalcLogic logic = spy(new CalcLogic());

   //SettingsDaoに対して利用する宣言
   //全メソッドに対してスタブを利用する宣言(設定を忘れると呼び出し結果でnullが返ったりする)
   //ミスで呼ばれたりすると困るような重要なメソッドがあるような場合には保険になるかも
   PowerMockito.mockStatic(SettingsDao.class);
   //設定をしたメソッドにだけスタブを利用する宣言
   //明示的に出来て便利だが、ミスで実メソッドが呼ばれて困る事があるかも
   //PowerMockito.spy(SettingsDao.class);

   when(SettingsDao.getCurrencySign()).thenReturn(" JPY");

   when(logic.calcPrice(200)).thenReturn(2999);
   //特定の引数に意味が無いような場合、以下にしておくとどん引数でもという条件となる
   //when(logic.calcPrice(anyInt())).thenReturn(2999);

   String calcPrice = logic.calcDispPrice(200);

   assertEquals("2999 JPY", calcPrice);

   //以下モックに対する検証
   //getCurrencySignが2回呼ばれたことを検証(1回しか呼ばれないので当然失敗する)
   PowerMockito.verifyStatic(times(2));
   SettingsDao.getCurrencySign();
 }
}