Blog by yui-Kitamura

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.

this code doesn't work
//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.

Sample test code
//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.

Sample for No argument at the test method
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 void testablePrivateMethod(int arg){
       try {
           Method method = Target.class.getDeclaredMethod("target", int.class);
           method.setAccessible(true);

           // does not need the instance
           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)

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 orginal Japanese version.

Please let's me know with following comment form, is this article helpful for you?


Add this article to はてなブックマーク


Comment form is HERE!!!!!