Things to keep in mind when testing private methods in JUnit

Just in case you really have to test private methods in JUnit. I forget how to implement it on a regular basis, so I'll never forget it if I write it firmly once. I will write it down with the motive.

General case

Consider a class with a private method like the one below.

public class MyClass {
    private String introduceMyself(String name){
        return "my name is " + name + ".";
    }
}

Testing of MyClass's private introduceMyself method can be done using reflection as follows:

import com.example.MyClass;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import static org.assertj.core.api.Assertions.assertThat;

public class MyClassTest {
    @Test
    void testIntroduceMyself01() throws Exception {
        MyClass targetClass = new MyClass();
        //Get information about the method under test
        Method method = MyClass.class.getDeclaredMethod("introduceMyself", String.class);
        //Remove access restrictions on methods
        method.setAccessible(true);
        //Method call
        String selfIntroduction = (String) method.invoke(targetClass,"John");
        //Assert the result
        assertThat(selfIntroduction).isEqualTo("my name is John.");
    }
}

Exception assertion thrown by a private method

The above is fine for normal test cases, but be careful when exceptions are thrown from private methods.

Exceptions that occur in Method # invoke will be wrapped in the InvocationTargetException class. Therefore, it is necessary to make an assertion after extracting the cause exception.

public class MyClass {
    private String introduceMyself(String name){
        //Throws an exception if the argument is an empty string
        if(name.isEmpty()){
            throw new IllegalArgumentException("argument must not be empty.");
        }
        return "my name is " + name + ".";
    }
}
public class MyClassTest {
    @Test
    void testIntroduceMyself02() throws Exception {
        MyClass targetClass = new MyClass();
        Method method = MyClass.class.getDeclaredMethod("introduceMyself", String.class);
        method.setAccessible(true);

        try {
            method.invoke(targetClass, "");
            fail("this code should not has been reached.");
        }catch (InvocationTargetException e) {
            //Assertion after fetching the cause exception with getCause
            assertThat(e.getCause()).isInstanceOf(IllegalArgumentException.class);
        }
    }
}

Pass null as an argument of private method

If you want to pass null as an argument of the method to be called by reflection, you need to pass an Object array that has null as an element, so be careful here as well.

method.invoke(targetClass, new Object[]{null});
//This is no good
method.invoke(targetClass, null);

Private methods should not be tested alone in the first place

As I said so far, private methods should not be unit tested in principle.

A private method is, so to speak, an internal process cut out from a public (protected) method. By forcibly testing internal processing with reflection, the test code becomes dependent on the internal implementation, which becomes a barrier when refactoring or adding functions.

Comprehensiveness can be tested via the calling method if it is a well-designed private method. If there is a branch that must be called individually for private methods, it is effectively dead code and you should review your implementation.

Summary

--Reflection required to call private method --To test the exception, getCause from InvocationTargetException. --When passing null to invoke, pass an Object array with null as an element --If you need to test a private method, revisit your design --In principle, private methods should be tested via the calling method.

Recommended Posts

Things to keep in mind when testing private methods in JUnit
Things to keep in mind when committing to CRuby
Things to keep in mind when adding war to dependency
Things to keep in mind when using if statements
Things to keep in mind when using Sidekiq with Rails
Things to keep in mind when combining if statements and logical operators
Things to keep in mind when using Apache PDFBox® with AWS Lambda
Test private methods in JUnit
Things to keep in mind when installing Jekyll on Windows and loading themes! !! Need tzinfo !!
[Java Bronze] 5 problems to keep in mind
How to test private methods with arrays or variadic arguments in JUnit
If you want to satisfy the test coverage of private methods in JUnit
[Comma (,) is strictly prohibited in the address! ] Things to keep in mind when applying for an exam at Pearson VUE
About validation methods in JUnit
Things to be aware of when writing code in Java
Things to note when using Spring AOP in Jersery resource classes
[Java] Test private methods with JUnit
Things to note in conditional expressions
How to filter JUnit Test in Gradle
How to fix system date in JUnit
How to test private scope with JUnit
How to set when "The constructor Empty () is not visible" occurs in junit