Test the PRIVATE method with jUnit
This article is translated and rewrited from Japanese article
Call a private method from the test class is impossible
"jUnit" is convenient to test the Java program. However, the test classes can NOT call any Private,Protected or package-private methods, because they (test class and orignal implemented class) are the different class.
//the test target class class Target{ /** I want to test this private method */ private void target(){ throw new RuntimeException(); } } //the smart test code .. class TargetTest{ @Test public void testTarget(){ Target t = new Target(); assertDoesNotThrow( ()->t.target() ); //compiling ERROR } }
There for, use the reflection.
The test code for test private method
See the sample code below.
//the test target class class Target{ /** I want to test this private method */ private int target(int arg){ return arg; } } //the test code class TargetTest{ /** call the private method: Target#target(int) * @param instance a instance that owns the target method * @param arg arguments for the target method */ private int testablePrivateMethod(Target instance, int arg){ int result; try { //"target" is a method name Method method = Target.class.getDeclaredMethod("target", int.class); method.setAccessible(true); //cast to orginal type, because #invoke returns Object 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 } }
I uploaded full source code to GitHub (commented with Japanese)
Explain for coding
I prepared the test target private method as get one argument and returns the value. This solution also works to the static methods or multiple arguments. (skip to no argument or multiple arguments)
To test the private method, define the method that calls the target private method in the test class.
At this time, I named it as testablePrivateMethod
but I'm not sure that there are any common rules for naming this.
Usually I name them which calls hogehoge
, callHogehoge
.
Define the calling method with arguments: first, the instance for the test target class. following, the arguments same as test target method.
On the line19, we get the test target method.
Method method = Target.class.getDeclaredMethod("target", int.class);
Set Target.class
with your target class.
The arguments of getDeclaredMethod
are, "method name" and "arguments define class".
With this parameters, this method will return the matched method (that you want to test).
With Method#invoke()
calling, the target method will be executed.
result = (int) method.invoke(instance, arg);
The method invoke()
returns the type Object
,
so that we cast to any type we want.
Variety of target method params pattern or else..
CASE: no argument, multiple arguments
//test target class Target{ private double target(int arg, double arg2){ return arg + arg2; } } //test code 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; } }
The method getDeclaredMethod
and invoke
are defined to accept
any number of classes for the second argument. This includes Zero.
So, we can write the same number of arguments with the test target methods arguments.
In a case, nothing with the second arguments. At the other case, a lot of arguments.
Method method = Target.class.getDeclaredMethod("target"); method.setAccessible(true); result = (int) method.invoke(instance);
CASE: target method is defined as static
//test target class Target{ private static void target(int arg){ System.out.print(arg); } } //test code class TargetTest{ /** call the private static method * @param arg arguments for the target method */ private int testablePrivateMethod(int arg){ try { Method method = Target.class.getDeclaredMethod("target", int.class); method.setAccessible(true); // does not need the instance return (int) method.invoke(Target.class, arg); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } } @Test public void testTarget(){ //Target t = new Target(); // not needed int arg = 27; assertEquals(arg, testablePrivateMethod(arg)); } }
In a case of testing the static method, the test code becomes a little smart.
Let's know that the first argument of Method#invoke
is the class, not a instance.
I uploaded the sample code set to GitHub with the static method version.(These are commented with Japanese)
- Repository
- Test target class(defined as static)
- test code using jUnit
Miscellaneous Notes
Check my custome "code-prettify"!
With in this article, I use the library "code-prettify" to show Java source code. Please check an article "code-prettifyをリファクタリングした(I refactored code-prettify)" that I'm introducing the refactored version. Feel free to use the refactored version!
I have running sample
I implemented in the Minecraft Spigot Server plugin, "the Airport Traffic Controller".
For English-speaking readers
Thank you for reading all this article. I hope that your trouble with Java programming be solved with this article.
I've got an idea when I was checking the Google Search Console to know how the people finding my articles, "Isn't there any demand about this jUnit solution with in English-speaking programmers?". So I tried to translate and a bit rewrite to English from original Japanese version.
Please let's me know with following comment form, is this article helpful for you?