既存システムに対してテストコードを書く(1/2)
JUnitを始めとするテストツールが充実した現在も、運用・保守の現場では、テストコードが存在しないJavaの既存システムを目にすることがあります。このようなシステムに対して、後付でテストコードを書こうとすると、大抵は以下の問題に突き当たって、モックの作成が難しくなります。
- テスト対象のコードが、staticメソッドを呼び出している
- テスト対象のコードが、技術的なAPIをnewして使っている
例えば、次のようなコードです。
public class InterestCalclator { public int calcInterest(int principal) { // 日数を取得 int days = DBAdapter.getCalcDaysFromDB(); // 実質年率の値をファイルから取得 BigDecimal interestRate = null; try { BufferedReader br = new BufferedReader(new FileReader("interestRate.txt")); String line = br.readLine(); interestRate = new BigDecimal(line); br.close(); } catch (IOException ex) { ex.printStackTrace(); } // 利子を計算 BigDecimal principalDec = BigDecimal.valueOf(principal); BigDecimal daysDec = BigDecimal.valueOf(days); BigDecimal result = principalDec.multiply(interestRate).multiply(daysDec.divide(BigDecimal.valueOf(365), 5, BigDecimal.ROUND_HALF_EVEN)); return result.round(new MathContext(0, RoundingMode.HALF_EVEN)).intValue(); } } public class DBAdapter { public static int getCalcDaysFromDB() { int days = 0; // JDBCを使って、データベースから値を取得 // 処理の記述は省略します return days; } }
InterestCalclatorクラスのcalcInterestメソッドがテスト対象のコードです。
メソッドの初めに、DBAdapterクラスのstaticメソッドのgetCalcDaysFromDBメソッドを呼び出して、日数を取得しています。getCalcDaysFromDBメソッドは、JDBCを使ってデータベースにアクセスします。テストするにはデータベースを準備する手間が発生しますので、getCalcDaysFromDBメソッドをモックに変更したいところです。
次に、BufferedReaderクラスやFileReaderクラスをnewしてオブジェクトを作成し、実質年率の値をファイルから読み取っています。テストするにはファイルの準備が必要になるため、これらのオブジェクトもモックに変更したいところです。
JMockItを使った解決
著名なモックフレームワークのEasyMockやMockitoでは、この問題は解決できませんが、後発のJMockItというモックフレームワークであれば解決することが出来ます。
JMockItを使うと、以下のようなテストコードが書けます。
public class InterestCalclatorTest { @Test public void testCalcInterest() throws Exception { new NonStrictExpectations(DBAdapter.class) { { DBAdapter.getCalcDaysFromDB(); result = 30; } }; new NonStrictExpectations() { BufferedReader br; FileReader fr; { br.readLine(); result = "0.032"; } }; InterestCalclator target = new InterestCalclator(); int interest = target.calcInterest(1000000); assertEquals(2630, interest); } }
NonStrictExpectationsというクラスは、JMockItが提供するクラスです。詳しい説明は省略しますが、DBAdapter.getCalcDaysFromDBメソッドと、BufferedReaderとFileReaderがモックにすり替えられています。
JMockItの解説は、ryoasaiさんやj5ik2oさんのブログに詳しく書かれていますので、興味のある方は参照することをお勧めします。
では、JMockItをどんどん使って既存システムのテストコードを書けばいいかと言うと、そうでもありません。「力の悪用の例(英語の原文はAn example of power corrupting)」というサブタイトルのModern Mocking Tools and Black Magicという記事に興味深い問題の指摘が書いてあります。
その内容については、次回のブログで触れたいと思います。