jUnitでprivateメソッドをテストする
There is an English version for this article.
privateメソッドのテストはひと手間かかる
Java環境でメソッド単位の挙動をテストするのに便利なjUnitですが、 クラス内のprivateメソッドは直接呼び出せないため、単純にはテストができません
//テスト対象
class Target{
private void target(){
throw new RuntimeException();
}
}
//理想のテストコード
class TargetTest{
@Test
public void testTarget(){
Target t = new Target();
assertDoesNotThrow( ()->t.target() ); //コンパイルエラー
}
}
ここで登場するのが、リフレクション(reflection)です
privateメソッドのテストコード
サンプルコードは以下の通りです
//テスト対象
class Target{
private int target(int arg){
return arg;
}
}
//テストコード
class TargetTest{
/** 可視度が private のメソッドを呼び出す
* @param instance 呼び出すメソッドを持っているクラスのインスタンス
* @param arg 呼び出すメソッドへ渡す引数
*/
private int testablePrivateMethod(Target instance, int arg){
int result;
try {
Method method = Target.class.getDeclaredMethod("target", int.class);
method.setAccessible(true);
result = (int) method.invoke(instance, arg);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
return result;
}
@Test
public void testTarget(){
Target t = new Target();
int arg = 27;
assertEquals(arg, testablePrivateMethod(t, arg)); // success
}
}
GitHubにサンプルコードをアップしてあります
テストコード解説
テスト対象はシンプルな引数を1つ持ち、値を返す仕様のprivateメソッドとしました
staticや引数の個数等への対応は後述しますのでご安心を
テストクラスに、「privateメソッドを呼び出すメソッド」を定義します。testablePrivateMethodとしましたが、名称はお任せします。
普段の私は、メソッドhogehogeを呼び出すメソッドはcallHogehogeみたいにしますが、
標準となるような命名規則は無いかと思われます。
引数は、「テスト対象のインスタンス」と、「メソッドに渡す引数」です。
17行目でテスト対象のprivateメソッドを取得します。
Method method = Target.class.getDeclaredMethod("target", int.class);
Target.classはテスト対象のクラスにしてください。
getDeclaredMethodの引数は、「メソッド名」「引数のclass」です。引数で指定した条件に合致するメソッドが、
テスト対象のクラスから探索されて、method変数に格納されます
実際に呼び出す処理がinvokeメソッドです
result = (int) method.invoke(instance, arg);
invokeは戻り値がObject型なので、取得したい型にキャストします
テスト対象のパターンによる変化
引数の個数、ゼロから複数
//テスト対象
class Target{
private double target(int arg, double arg2){
return arg + arg2;
}
}
//テストコード
class TargetTest{
private int testablePrivateMethod(Target instance, int arg, double arg2){
int result;
try {
Method method = Target.class.getDeclaredMethod("target", int.class, double.class);
method.setAccessible(true);
result = (int) method.invoke(instance, arg, arg2);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
return result;
}
}
getDeclaredMethod,invokeは引数の個数が0を含む任意個数が許容されています。
よって、テスト対象のメソッドの引数の個数に合わせて、無記載から必要数の列挙まで、なんとでも認められます
Method method = Target.class.getDeclaredMethod("target");
method.setAccessible(true);
result = (int) method.invoke(instance);
staticメソッドのテスト
//テスト対象
class Target{
private static int targetMtd(int arg){
System.out.print(arg);
return arg;
}
}
//テストコード
class TargetTest{
/** 可視度が private のstaticメソッドを呼び出す
* @param instance 呼び出すメソッドを持っているクラスのインスタンス
* @param arg 呼び出すメソッドへ渡す引数
*/
private int testablePrivateMethod(int arg){
try {
Method method = Target.class.getDeclaredMethod("targetMtd", int.class);
method.setAccessible(true);
return (int) method.invoke(Target.class, arg);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
@Test
public void testTarget(){
// Target t = new Target();
int arg = 27;
assertEquals(arg, testablePrivateMethod(arg));
}
}
staticなメソッドについてはインスタンス化の手間が無い分、多少シンプルになっています。
invokeメソッドの引数もインスタンスではなく、テスト対象のクラス自体を渡します
staticのバージョンもGitHubにサンプルコードをアップしてあります
後記
初めてのJavaの技術記事になりました。今回は特段バージョンの影響が無さそうなネタなので 前提環境とかは省略してしまいましたが、きちんと検証してから投稿したいものです
一度組み方が分かってしまえばいつでも活用できる物なので、自分用の備忘録としてもまとめた次第です。
わたしの公開しているプログラムではMinecraftのプラグイン
とかで組み込んでいたりしますが、使う都度「どうだっけー」と探すのも面倒で…。
Javaプログラマの誰かのお役に立てば良いな、と思います
猫が綴る雑多なブログ
RSS配信中