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プログラマの誰かのお役に立てば良いな、と思います