【Java】プライベートメソッドをテストする方法【リフレクション】

単体テストを書くとき、よく問題となるのがPrivateメソッドの扱いです。PublicメソッドだけでなくPrivateメソッドまでテストする場合、大まかに分けて2つのやり方があります。

  • プロダクトコードを書き換えてテストする
  • プロダクトコードは書き換えずにテストの仕方を工夫する

プロダクトコードとは、(テストコードに対して)本体のコードのことで、それを書き換えるというのは、単純にprivateをpublicに書き換えるということです。簡単ですが、ソースの管理をきちんとしないと、プロダクトコードやテストの信頼性が損なわれます。


それに対して、テストの仕方を工夫するやり方では、プロダクトコードは変更しません。その代わり、テストコードでリフレクションという技術を使います。

リフレクション (reflection) とは、プログラムの実行過程でプログラム自身の構造を読み取ったり書き換えたりする技術のことである。
リフレクション (情報工学) - Wikipedia


以下は具体例。


■プロダクトコード

public class ReflectionTarget {
  private String privateMethod() {
    return "private!";
  } 
}

文字列を返すだけのPrivateメソッドを定義します。


■テストコード

import java.lang.reflect.Method;
import junit.framework.TestCase;

// テストクラス
public class ReflectionTargetTest extends TestCase {

  // テストメソッド
  public void testPublicMethod() throws Exception {

    // クラス取得
    Class<ReflectionTarget> c = ReflectionTarget.class;

    // メソッド取得(メソッド名を文字列として指定)
    // 引数がある場合は第二引数に指定
    Method m = c.getDeclaredMethod("privateMethod");

    // Privateメソッドの場合、以下の設定が必要
    m.setAccessible(true);

    // 実行(引数がある場合、第二引数以降に指定)
    String ret = (String) m.invoke(c.newInstance());

    // 比較
    assertEquals("private!", ret);
  }
}
  • まず、対象のクラスから、クラス情報を取得します。
  • クラス情報から、メソッドを取得します。
  • Privateメソッドを取得する場合、getDeclaredMethodメソッドを使用します。
  • その際、メソッド名を引数として指定します。
  • 引数を受け取るメソッドの場合、第二引数にクラス配列を指定します。
  • Privateメソッドを実行する場合、setAccessible(true)します。
  • 取得したメソッドをinvokeメソッドで実行します。
  • その際、第一引数には対象クラスのインスタンスを指定します。
  • 引数を渡して実行する場合、第二引数以降に指定します。


ちなみに引数ありはこんな感じです。

  private String addString(String str1, String str2) {
    return str1 + str2;
  } 

なら、

  public void testAddString() throws Exception {
    
    // 引数の分、クラス型の配列を定義
    Class[] args = { String.class, String.class };
    
    Class<ReflectionTarget> c = ReflectionTarget.class;

    // 第二引数にクラス配列を指定
    Method m = c.getDeclaredMethod("addString", args);

    m.setAccessible(true);

    String str1 = "Java, Java, ";
    String str2 = "Java Java jing-jing-jing";

    // 第二引数以降にメソッドに渡す引数を師弟
    String ret = (String) m.invoke(c.newInstance(), str1, str2);

    assertEquals(str1 + str2, ret);
  }

これでプライベートなメソッドもテストできます。
が、かなり面倒なのでプラグインとか使った方がいいかもです。


目指せテストマスター